home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
a_utils
/
_archvrs
/
unix
/
tar_1_11.sha
< prev
next >
Wrap
Text File
|
1994-01-23
|
792KB
|
27,662 lines
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: tar.c create.c extract.c buffer.c getoldopt.c update.c
# gnu.c mangle.c version.c list.c names.c diffarch.c port.c
# fnmatch.c getopt.c malloc.c getopt1.c regex.c getdate.y getdate.c
# alloca.c README INSTALL NEWS COPYING ChangeLog Makefile.in
# makefile.pc configure configure.in tar.h fnmatch.h pathmax.h
# port.h open3.h getopt.h regex.h rmt.h rmt.c rtapelib.c msd_dir.h
# msd_dir.c tcexparg.c level-0 level-1 backup-specs dump-remind
# testpad.c getpagesize.h
# Wrapped by mib@geech.gnu.ai.mit.edu on Thu Mar 25 14:01:53 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'tar.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'tar.c'\"
else
echo shar: Extracting \"'tar.c'\" \(36191 characters\)
sed "s/^X//" >'tar.c' <<'END_OF_FILE'
X/* Tar -- a tape archiver.
X Copyright (C) 1988, 1992, 1993 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/*
X * A tar (tape archiver) program.
X *
X * Written by John Gilmore, ihnp4!hoptoad!gnu, starting 25 Aug 85.
X */
X
X#include <stdio.h>
X#include <sys/types.h> /* Needed for typedefs in tar.h */
X#include "getopt.h"
X#include "regex.h"
X
X/*
X * The following causes "tar.h" to produce definitions of all the
X * global variables, rather than just "extern" declarations of them.
X */
X#define TAR_EXTERN /**/
X#include "tar.h"
X
X#include "port.h"
X#include "fnmatch.h"
X
X/*
X * We should use a conversion routine that does reasonable error
X * checking -- atoi doesn't. For now, punt. FIXME.
X */
X#define intconv atoi
XPTR ck_malloc ();
XPTR ck_realloc ();
Xextern int getoldopt ();
Xextern void read_and ();
Xextern void list_archive ();
Xextern void extract_archive ();
Xextern void diff_archive ();
Xextern void create_archive ();
Xextern void update_archive ();
Xextern void junk_archive ();
Xextern void init_volume_number ();
Xextern void closeout_volume_number ();
X
X/* JF */
Xextern time_t get_date ();
X
Xtime_t new_time;
X
Xstatic FILE *namef; /* File to read names from */
Xstatic char **n_argv; /* Argv used by name routines */
Xstatic int n_argc; /* Argc used by name routines */
Xstatic char **n_ind; /* Store an array of names */
Xstatic int n_indalloc; /* How big is the array? */
Xstatic int n_indused; /* How many entries does it have? */
Xstatic int n_indscan; /* How many of the entries have we scanned? */
X
X
Xextern FILE *msg_file;
X
Xint check_exclude ();
Xvoid add_exclude ();
Xvoid add_exclude_file ();
Xvoid addname ();
Xvoid describe ();
Xvoid diff_init ();
Xvoid extr_init ();
Xint is_regex ();
Xvoid name_add ();
Xvoid name_init ();
Xvoid options ();
Xchar *un_quote_string ();
X
X#ifndef S_ISLNK
X#define lstat stat
X#endif
X
X#ifndef DEFBLOCKING
X#define DEFBLOCKING 20
X#endif
X
X#ifndef DEF_AR_FILE
X#define DEF_AR_FILE "tar.out"
X#endif
X
X/* For long options that unconditionally set a single flag, we have getopt
X do it. For the others, we share the code for the equivalent short
X named option, the name of which is stored in the otherwise-unused `val'
X field of the `struct option'; for long options that have no equivalent
X short option, we use nongraphic characters as pseudo short option
X characters, starting (for no particular reason) with character 10. */
X
Xstruct option long_options[] =
X{
X {"create", 0, 0, 'c'},
X {"append", 0, 0, 'r'},
X {"extract", 0, 0, 'x'},
X {"get", 0, 0, 'x'},
X {"list", 0, 0, 't'},
X {"update", 0, 0, 'u'},
X {"catenate", 0, 0, 'A'},
X {"concatenate", 0, 0, 'A'},
X {"compare", 0, 0, 'd'},
X {"diff", 0, 0, 'd'},
X {"delete", 0, 0, 14},
X {"help", 0, 0, 12},
X
X {"null", 0, 0, 16},
X {"directory", 1, 0, 'C'},
X {"record-number", 0, &f_sayblock, 1},
X {"files-from", 1, 0, 'T'},
X {"label", 1, 0, 'V'},
X {"exclude-from", 1, 0, 'X'},
X {"exclude", 1, 0, 15},
X {"file", 1, 0, 'f'},
X {"block-size", 1, 0, 'b'},
X {"version", 0, 0, 11},
X {"verbose", 0, 0, 'v'},
X {"totals", 0, &f_totals, 1},
X
X {"read-full-blocks", 0, &f_reblock, 1},
X {"starting-file", 1, 0, 'K'},
X {"to-stdout", 0, &f_exstdout, 1},
X {"ignore-zeros", 0, &f_ignorez, 1},
X {"keep-old-files", 0, 0, 'k'},
X {"same-permissions", 0, &f_use_protection, 1},
X {"preserve-permissions", 0, &f_use_protection, 1},
X {"modification-time", 0, &f_modified, 1},
X {"preserve", 0, 0, 10},
X {"same-order", 0, &f_sorted_names, 1},
X {"same-owner", 0, &f_do_chown, 1},
X {"preserve-order", 0, &f_sorted_names, 1},
X
X {"newer", 1, 0, 'N'},
X {"after-date", 1, 0, 'N'},
X {"newer-mtime", 1, 0, 13},
X {"incremental", 0, 0, 'G'},
X {"listed-incremental", 1, 0, 'g'},
X {"multi-volume", 0, &f_multivol, 1},
X {"info-script", 1, 0, 'F'},
X {"new-volume-script", 1, 0, 'F'},
X {"absolute-paths", 0, &f_absolute_paths, 1},
X {"interactive", 0, &f_confirm, 1},
X {"confirmation", 0, &f_confirm, 1},
X
X {"verify", 0, &f_verify, 1},
X {"dereference", 0, &f_follow_links, 1},
X {"one-file-system", 0, &f_local_filesys, 1},
X {"old-archive", 0, 0, 'o'},
X {"portability", 0, 0, 'o'},
X {"compress", 0, 0, 'Z'},
X {"uncompress", 0, 0, 'Z'},
X {"block-compress", 0, &f_compress_block, 1},
X {"gzip", 0, 0, 'z'},
X {"ungzip", 0, 0, 'z'},
X {"use-compress-program", 1, 0, 18},
X
X
X {"same-permissions", 0, &f_use_protection, 1},
X {"sparse", 0, &f_sparse_files, 1},
X {"tape-length", 1, 0, 'L'},
X {"remove-files", 0, &f_remove_files, 1},
X {"ignore-failed-read", 0, &f_ignore_failed_read, 1},
X {"checkpoint", 0, &f_checkpoint, 1},
X {"show-omitted-dirs", 0, &f_show_omitted_dirs, 1},
X {"volno-file", 1, 0, 17},
X {"force-local", 0, &f_force_local, 1},
X {"atime-preserve", 0, &f_atime_preserve, 1},
X
X {0, 0, 0, 0}
X};
X
X/*
X * Main routine for tar.
X */
Xvoid
Xmain (argc, argv)
X int argc;
X char **argv;
X{
X extern char version_string[];
X
X tar = argv[0]; /* JF: was "tar" Set program name */
X filename_terminator = '\n';
X errors = 0;
X
X options (argc, argv);
X
X if (!n_argv)
X name_init (argc, argv);
X
X if (f_volno_file)
X init_volume_number ();
X
X switch (cmd_mode)
X {
X case CMD_CAT:
X case CMD_UPDATE:
X case CMD_APPEND:
X update_archive ();
X break;
X case CMD_DELETE:
X junk_archive ();
X break;
X case CMD_CREATE:
X create_archive ();
X if (f_totals)
X fprintf (stderr, "Total bytes written: %d\n", tot_written);
X break;
X case CMD_EXTRACT:
X if (f_volhdr)
X {
X const char *err;
X label_pattern = (struct re_pattern_buffer *)
X ck_malloc (sizeof *label_pattern);
X err = re_compile_pattern (f_volhdr, strlen (f_volhdr),
X label_pattern);
X if (err)
X {
X fprintf (stderr, "Bad regular expression: %s\n",
X err);
X errors++;
X break;
X }
X
X }
X extr_init ();
X read_and (extract_archive);
X break;
X case CMD_LIST:
X if (f_volhdr)
X {
X const char *err;
X label_pattern = (struct re_pattern_buffer *)
X ck_malloc (sizeof *label_pattern);
X err = re_compile_pattern (f_volhdr, strlen (f_volhdr),
X label_pattern);
X if (err)
X {
X fprintf (stderr, "Bad regular expression: %s\n",
X err);
X errors++;
X break;
X }
X }
X read_and (list_archive);
X#if 0
X if (!errors)
X errors = different;
X#endif
X break;
X case CMD_DIFF:
X diff_init ();
X read_and (diff_archive);
X break;
X case CMD_VERSION:
X fprintf (stderr, "%s\n", version_string);
X break;
X case CMD_NONE:
X msg ("you must specify exactly one of the r, c, t, x, or d options\n");
X fprintf (stderr, "For more information, type ``%s --help''.\n", tar);
X exit (EX_ARGSBAD);
X }
X if (f_volno_file)
X closeout_volume_number ();
X exit (errors);
X /* NOTREACHED */
X}
X
X
X/*
X * Parse the options for tar.
X */
Xvoid
Xoptions (argc, argv)
X int argc;
X char **argv;
X{
X register int c; /* Option letter */
X int ind = -1;
X
X /* Set default option values */
X blocking = DEFBLOCKING; /* From Makefile */
X ar_files = (char **) ck_malloc (sizeof (char *) * 10);
X ar_files_len = 10;
X n_ar_files = 0;
X cur_ar_file = 0;
X
X /* Parse options */
X while ((c = getoldopt (argc, argv,
X "-01234567Ab:BcC:df:F:g:GhikK:lL:mMN:oOpPrRsStT:uvV:wWxX:zZ",
X long_options, &ind)) != EOF)
X {
X switch (c)
X {
X case 0: /* long options that set a single flag */
X break;
X case 1:
X /* File name or non-parsed option */
X name_add (optarg);
X break;
X case 'C':
X name_add ("-C");
X name_add (optarg);
X break;
X case 10: /* preserve */
X f_use_protection = f_sorted_names = 1;
X break;
X case 11:
X if (cmd_mode != CMD_NONE)
X goto badopt;
X cmd_mode = CMD_VERSION;
X break;
X case 12: /* help */
X printf ("This is GNU tar, the tape archiving program.\n");
X describe ();
X exit (1);
X case 13:
X f_new_files++;
X goto get_newer;
X
X case 14: /* Delete in the archive */
X if (cmd_mode != CMD_NONE)
X goto badopt;
X cmd_mode = CMD_DELETE;
X break;
X
X case 15:
X f_exclude++;
X add_exclude (optarg);
X break;
X
X case 16: /* -T reads null terminated filenames. */
X filename_terminator = '\0';
X break;
X
X case 17:
X f_volno_file = optarg;
X break;
X
X case 18:
X if (f_compressprog)
X {
X msg ("Only one compression option permitted\n");
X exit (EX_ARGSBAD);
X }
X f_compressprog = optarg;
X break;
X
X case 'g': /* We are making a GNU dump; save
X directories at the beginning of
X the archive, and include in each
X directory its contents */
X if (f_oldarch)
X goto badopt;
X f_gnudump++;
X gnu_dumpfile = optarg;
X break;
X
X
X case '0':
X case '1':
X case '2':
X case '3':
X case '4':
X case '5':
X case '6':
X case '7':
X {
X /* JF this'll have to be modified for other
X systems, of course! */
X int d, add;
X static char buf[50];
X
X d = getoldopt (argc, argv, "lmh");
X#ifdef MAYBEDEF
X sprintf (buf, "/dev/rmt/%d%c", c, d);
X#else
X#ifndef LOW_NUM
X#define LOW_NUM 0
X#define MID_NUM 8
X#define HGH_NUM 16
X#endif
X if (d == 'l')
X add = LOW_NUM;
X else if (d == 'm')
X add = MID_NUM;
X else if (d == 'h')
X add = HGH_NUM;
X else
X goto badopt;
X
X sprintf (buf, "/dev/rmt%d", add + c - '0');
X#endif
X if (n_ar_files == ar_files_len)
X ar_files
X = (char **)
X ck_malloc (sizeof (char *)
X * (ar_files_len *= 2));
X ar_files[n_ar_files++] = buf;
X }
X break;
X
X case 'A': /* Arguments are tar files,
X just cat them onto the end
X of the archive. */
X if (cmd_mode != CMD_NONE)
X goto badopt;
X cmd_mode = CMD_CAT;
X break;
X
X case 'b': /* Set blocking factor */
X blocking = intconv (optarg);
X break;
X
X case 'B': /* Try to reblock input */
X f_reblock++; /* For reading 4.2BSD pipes */
X break;
X
X case 'c': /* Create an archive */
X if (cmd_mode != CMD_NONE)
X goto badopt;
X cmd_mode = CMD_CREATE;
X break;
X
X#if 0
X case 'C':
X if (chdir (optarg) < 0)
X msg_perror ("Can't change directory to %d", optarg);
X break;
X#endif
X
X case 'd': /* Find difference tape/disk */
X if (cmd_mode != CMD_NONE)
X goto badopt;
X cmd_mode = CMD_DIFF;
X break;
X
X case 'f': /* Use ar_file for the archive */
X if (n_ar_files == ar_files_len)
X ar_files
X = (char **) ck_malloc (sizeof (char *)
X * (ar_files_len *= 2));
X
X ar_files[n_ar_files++] = optarg;
X break;
X
X case 'F':
X /* Since -F is only useful with -M , make it implied */
X f_run_script_at_end++;/* run this script at the end */
X info_script = optarg; /* of each tape */
X f_multivol++;
X break;
X
X case 'G': /* We are making a GNU dump; save
X directories at the beginning of
X the archive, and include in each
X directory its contents */
X if (f_oldarch)
X goto badopt;
X f_gnudump++;
X gnu_dumpfile = 0;
X break;
X
X case 'h':
X f_follow_links++; /* follow symbolic links */
X break;
X
X case 'i':
X f_ignorez++; /* Ignore zero records (eofs) */
X /*
X * This can't be the default, because Unix tar
X * writes two records of zeros, then pads out the
X * block with garbage.
X */
X break;
X
X case 'k': /* Don't overwrite files */
X#ifdef NO_OPEN3
X msg ("can't keep old files on this system");
X exit (EX_ARGSBAD);
X#else
X f_keep++;
X#endif
X break;
X
X case 'K':
X f_startfile++;
X addname (optarg);
X break;
X
X case 'l': /* When dumping directories, don't
X dump files/subdirectories that are
X on other filesystems. */
X f_local_filesys++;
X break;
X
X case 'L':
X tape_length = intconv (optarg);
X f_multivol++;
X break;
X case 'm':
X f_modified++;
X break;
X
X case 'M': /* Make Multivolume archive:
X When we can't write any more
X into the archive, re-open it,
X and continue writing */
X f_multivol++;
X break;
X
X case 'N': /* Only write files newer than X */
X get_newer:
X f_new_files++;
X new_time = get_date (optarg, (PTR) 0);
X if (new_time == (time_t) - 1)
X {
X msg ("invalid date format `%s'", optarg);
X exit (EX_ARGSBAD);
X }
X break;
X
X case 'o': /* Generate old archive */
X if (f_gnudump /* || f_dironly */ )
X goto badopt;
X f_oldarch++;
X break;
X
X case 'O':
X f_exstdout++;
X break;
X
X case 'p':
X f_use_protection++;
X break;
X
X case 'P':
X f_absolute_paths++;
X break;
X
X case 'r': /* Append files to the archive */
X if (cmd_mode != CMD_NONE)
X goto badopt;
X cmd_mode = CMD_APPEND;
X break;
X
X case 'R':
X f_sayblock++; /* Print block #s for debug */
X break; /* of bad tar archives */
X
X case 's':
X f_sorted_names++; /* Names to extr are sorted */
X break;
X
X case 'S': /* deal with sparse files */
X f_sparse_files++;
X break;
X case 't':
X if (cmd_mode != CMD_NONE)
X goto badopt;
X cmd_mode = CMD_LIST;
X f_verbose++; /* "t" output == "cv" or "xv" */
X break;
X
X case 'T':
X name_file = optarg;
X f_namefile++;
X break;
X
X case 'u': /* Append files to the archive that
X aren't there, or are newer than the
X copy in the archive */
X if (cmd_mode != CMD_NONE)
X goto badopt;
X cmd_mode = CMD_UPDATE;
X break;
X
X case 'v':
X f_verbose++;
X break;
X
X case 'V':
X f_volhdr = optarg;
X break;
X
X case 'w':
X f_confirm++;
X break;
X
X case 'W':
X f_verify++;
X break;
X
X case 'x': /* Extract files from the archive */
X if (cmd_mode != CMD_NONE)
X goto badopt;
X cmd_mode = CMD_EXTRACT;
X break;
X
X case 'X':
X f_exclude++;
X add_exclude_file (optarg);
X break;
X
X case 'z':
X if (f_compressprog)
X {
X msg ("Only one compression option permitted\n");
X exit (EX_ARGSBAD);
X }
X f_compressprog = "gzip";
X break;
X
X case 'Z':
X if (f_compressprog)
X {
X msg ("Only one compression option permitted\n");
X exit (EX_ARGSBAD);
X }
X f_compressprog = "compress";
X break;
X
X case '?':
X badopt:
X msg ("Unknown option. Use '%s --help' for a complete list of options.", tar);
X exit (EX_ARGSBAD);
X
X }
X }
X
X blocksize = blocking * RECORDSIZE;
X if (n_ar_files == 0)
X {
X n_ar_files = 1;
X ar_files[0] = getenv ("TAPE"); /* From environment, or */
X if (ar_files[0] == 0)
X ar_files[0] = DEF_AR_FILE; /* From Makefile */
X }
X if (n_ar_files > 1 && !f_multivol)
X {
X msg ("Multiple archive files requires --multi-volume\n");
X exit (EX_ARGSBAD);
X }
X if (f_compress_block && !f_compressprog)
X {
X msg ("You must use a compression option (--gzip, --compress\n\
Xor --use-compress-program) with --block-compress.\n");
X exit (EX_ARGSBAD);
X }
X}
X
X
X/*
X * Print as much help as the user's gonna get.
X *
X * We have to sprinkle in the KLUDGE lines because too many compilers
X * cannot handle character strings longer than about 512 bytes. Yuk!
X * In particular, MS-DOS and Xenix MSC and PDP-11 V7 Unix have this
X * problem.
X */
Xvoid
Xdescribe ()
X{
X puts ("choose one of the following:");
X fputs ("\
X-A, --catenate,\n\
X --concatenate append tar files to an archive\n\
X-c, --create create a new archive\n\
X-d, --diff,\n\
X --compare find differences between archive and file system\n\
X--delete delete from the archive (not for use on mag tapes!)\n\
X-r, --append append files to the end of an archive\n\
X-t, --list list the contents of an archive\n\
X-u, --update only append files that are newer than copy in archive\n\
X-x, --extract,\n\
X --get extract files from an archive\n", stdout);
X
X fprintf (stdout, "\
XOther options:\n\
X--atime-preserve don't change access times on dumped files\n\
X-b, --block-size N block size of Nx512 bytes (default N=%d)\n", DEFBLOCKING);
X fputs ("\
X-B, --read-full-blocks reblock as we read (for reading 4.2BSD pipes)\n\
X-C, --directory DIR change to directory DIR\n\
X--checkpoint print directory names while reading the archive\n\
X", stdout); /* KLUDGE */
X fprintf (stdout, "\
X-f, --file [HOSTNAME:]F use archive file or device F (default %s)\n",
X DEF_AR_FILE);
X fputs ("\
X--force-local archive file is local even if has a colon\n\
X-F, --info-script F\n\
X --new-volume-script F run script at end of each tape (implies -M)\n\
X-G, --incremental create/list/extract old GNU-format incremental backup\n\
X-g, --listed-incremental F create/list/extract new GNU-format incremental backup\n\
X-h, --dereference don't dump symlinks; dump the files they point to\n\
X-i, --ignore-zeros ignore blocks of zeros in archive (normally mean EOF)\n\
X--ignore-failed-read don't exit with non-zero status on unreadable files\n\
X-k, --keep-old-files keep existing files; don't overwrite them from archive\n\
X-K, --starting-file F begin at file F in the archive\n\
X-l, --one-file-system stay in local file system when creating an archive\n\
X-L, --tape-length N change tapes after writing N*1024 bytes\n\
X", stdout); /* KLUDGE */
X fputs ("\
X-m, --modification-time don't extract file modified time\n\
X-M, --multi-volume create/list/extract multi-volume archive\n\
X-N, --after-date DATE,\n\
X --newer DATE only store files newer than DATE\n\
X-o, --old-archive,\n\
X --portability write a V7 format archive, rather than ANSI format\n\
X-O, --to-stdout extract files to standard output\n\
X-p, --same-permissions,\n\
X --preserve-permissions extract all protection information\n\
X-P, --absolute-paths don't strip leading `/'s from file names\n\
X--preserve like -p -s\n\
X", stdout); /* KLUDGE */
X fputs ("\
X-R, --record-number show record number within archive with each message\n\
X--remove-files remove files after adding them to the archive\n\
X-s, --same-order,\n\
X --preserve-order list of names to extract is sorted to match archive\n\
X--same-owner create extracted files with the same ownership \n\
X-S, --sparse handle sparse files efficiently\n\
X-T, --files-from F get names to extract or create from file F\n\
X--null -T reads null-terminated names, disable -C\n\
X--totals print total bytes written with --create\n\
X-v, --verbose verbosely list files processed\n\
X-V, --label NAME create archive with volume name NAME\n\
X--version print tar program version number\n\
X-w, --interactive,\n\
X --confirmation ask for confirmation for every action\n\
X", stdout); /* KLUDGE */
X fputs ("\
X-W, --verify attempt to verify the archive after writing it\n\
X--exclude FILE exclude file FILE\n\
X-X, --exclude-from FILE exclude files listed in FILE\n\
X-Z, --compress,\n\
X --uncompress filter the archive through compress\n\
X-z, --gzip,\n\
X --ungzip filter the archive through gzip\n\
X--use-compress-program PROG\n\
X filter the archive through PROG (which must accept -d)\n\
X--block-compress block the output of compression program for tapes\n\
X-[0-7][lmh] specify drive and density\n\
X", stdout);
X}
X
Xvoid
Xname_add (name)
X char *name;
X{
X if (n_indalloc == n_indused)
X {
X n_indalloc += 10;
X n_ind = (char **) (n_indused ? ck_realloc (n_ind, n_indalloc * sizeof (char *)): ck_malloc (n_indalloc * sizeof (char *)));
X }
X n_ind[n_indused++] = name;
X}
X
X/*
X * Set up to gather file names for tar.
X *
X * They can either come from stdin or from argv.
X */
Xvoid
Xname_init (argc, argv)
X int argc;
X char **argv;
X{
X
X if (f_namefile)
X {
X if (optind < argc)
X {
X msg ("too many args with -T option");
X exit (EX_ARGSBAD);
X }
X if (!strcmp (name_file, "-"))
X {
X namef = stdin;
X }
X else
X {
X namef = fopen (name_file, "r");
X if (namef == NULL)
X {
X msg_perror ("can't open file %s", name_file);
X exit (EX_BADFILE);
X }
X }
X }
X else
X {
X /* Get file names from argv, after options. */
X n_argc = argc;
X n_argv = argv;
X }
X}
X
X/* Read the next filename read from STREAM and null-terminate it.
X Put it into BUFFER, reallocating and adjusting *PBUFFER_SIZE if necessary.
X Return the new value for BUFFER, or NULL at end of file. */
X
Xchar *
Xread_name_from_file (buffer, pbuffer_size, stream)
X char *buffer;
X size_t *pbuffer_size;
X FILE *stream;
X{
X register int c;
X register int indx = 0;
X register size_t buffer_size = *pbuffer_size;
X
X while ((c = getc (stream)) != EOF && c != filename_terminator)
X {
X if (indx == buffer_size)
X {
X buffer_size += NAMSIZ;
X buffer = ck_realloc (buffer, buffer_size + 2);
X }
X buffer[indx++] = c;
X }
X if (indx == 0 && c == EOF)
X return NULL;
X if (indx == buffer_size)
X {
X buffer_size += NAMSIZ;
X buffer = ck_realloc (buffer, buffer_size + 2);
X }
X buffer[indx] = '\0';
X *pbuffer_size = buffer_size;
X return buffer;
X}
X
X/*
X * Get the next name from argv or the name file.
X *
X * Result is in static storage and can't be relied upon across two calls.
X *
X * If CHANGE_DIRS is non-zero, treat a filename of the form "-C" as
X * meaning that the next filename is the name of a directory to change to.
X * If `filename_terminator' is '\0', CHANGE_DIRS is effectively always 0.
X */
X
Xchar *
Xname_next (change_dirs)
X int change_dirs;
X{
X static char *buffer; /* Holding pattern */
X static int buffer_siz;
X register char *p;
X register char *q = 0;
X register int next_name_is_dir = 0;
X extern char *un_quote_string ();
X
X if (buffer_siz == 0)
X {
X buffer = ck_malloc (NAMSIZ + 2);
X buffer_siz = NAMSIZ;
X }
X if (filename_terminator == '\0')
X change_dirs = 0;
Xtryagain:
X if (namef == NULL)
X {
X if (n_indscan < n_indused)
X p = n_ind[n_indscan++];
X else if (optind < n_argc)
X /* Names come from argv, after options */
X p = n_argv[optind++];
X else
X {
X if (q)
X msg ("Missing filename after -C");
X return NULL;
X }
X
X /* JF trivial support for -C option. I don't know if
X chdir'ing at this point is dangerous or not.
X It seems to work, which is all I ask. */
X if (change_dirs && !q && p[0] == '-' && p[1] == 'C' && p[2] == '\0')
X {
X q = p;
X goto tryagain;
X }
X if (q)
X {
X if (chdir (p) < 0)
X msg_perror ("Can't chdir to %s", p);
X q = 0;
X goto tryagain;
X }
X /* End of JF quick -C hack */
X
X#if 0
X if (f_exclude && check_exclude (p))
X goto tryagain;
X#endif
X return un_quote_string (p);
X }
X while (p = read_name_from_file (buffer, &buffer_siz, namef))
X {
X buffer = p;
X if (*p == '\0')
X continue; /* Ignore empty lines. */
X q = p + strlen (p) - 1;
X while (q > p && *q == '/')/* Zap trailing "/"s. */
X *q-- = '\0';
X if (change_dirs && next_name_is_dir == 0
X && p[0] == '-' && p[1] == 'C' && p[2] == '\0')
X {
X next_name_is_dir = 1;
X goto tryagain;
X }
X if (next_name_is_dir)
X {
X if (chdir (p) < 0)
X msg_perror ("Can't change to directory %s", p);
X next_name_is_dir = 0;
X goto tryagain;
X }
X#if 0
X if (f_exclude && check_exclude (p))
X goto tryagain;
X#endif
X return un_quote_string (p);
X }
X return NULL;
X}
X
X
X/*
X * Close the name file, if any.
X */
Xvoid
Xname_close ()
X{
X
X if (namef != NULL && namef != stdin)
X fclose (namef);
X}
X
X
X/*
X * Gather names in a list for scanning.
X * Could hash them later if we really care.
X *
X * If the names are already sorted to match the archive, we just
X * read them one by one. name_gather reads the first one, and it
X * is called by name_match as appropriate to read the next ones.
X * At EOF, the last name read is just left in the buffer.
X * This option lets users of small machines extract an arbitrary
X * number of files by doing "tar t" and editing down the list of files.
X */
Xvoid
Xname_gather ()
X{
X register char *p;
X static struct name *namebuf; /* One-name buffer */
X static namelen;
X static char *chdir_name;
X
X if (f_sorted_names)
X {
X if (!namelen)
X {
X namelen = NAMSIZ;
X namebuf = (struct name *) ck_malloc (sizeof (struct name) + NAMSIZ);
X }
X p = name_next (0);
X if (p)
X {
X if (*p == '-' && p[1] == 'C' && p[2] == '\0')
X {
X chdir_name = name_next (0);
X p = name_next (0);
X if (!p)
X {
X msg ("Missing file name after -C");
X exit (EX_ARGSBAD);
X }
X namebuf->change_dir = chdir_name;
X }
X namebuf->length = strlen (p);
X if (namebuf->length >= namelen)
X {
X namebuf = (struct name *) ck_realloc (namebuf, sizeof (struct name) + namebuf->length);
X namelen = namebuf->length;
X }
X strncpy (namebuf->name, p, namebuf->length);
X namebuf->name[namebuf->length] = 0;
X namebuf->next = (struct name *) NULL;
X namebuf->found = 0;
X namelist = namebuf;
X namelast = namelist;
X }
X return;
X }
X
X /* Non sorted names -- read them all in */
X while (p = name_next (0))
X addname (p);
X}
X
X/*
X * Add a name to the namelist.
X */
Xvoid
Xaddname (name)
X char *name; /* pointer to name */
X{
X register int i; /* Length of string */
X register struct name *p; /* Current struct pointer */
X static char *chdir_name;
X char *new_name ();
X
X if (name[0] == '-' && name[1] == 'C' && name[2] == '\0')
X {
X chdir_name = name_next (0);
X name = name_next (0);
X if (!chdir_name)
X {
X msg ("Missing file name after -C");
X exit (EX_ARGSBAD);
X }
X if (chdir_name[0] != '/')
X {
X char *path = ck_malloc (PATH_MAX);
X#if defined(__MSDOS__) || defined(HAVE_GETCWD) || defined(_POSIX_VERSION)
X if (!getcwd (path, PATH_MAX))
X {
X msg ("Couldn't get current directory.");
X exit (EX_SYSTEM);
X }
X#else
X char *getwd ();
X
X if (!getwd (path))
X {
X msg ("Couldn't get current directory: %s", path);
X exit (EX_SYSTEM);
X }
X#endif
X chdir_name = new_name (path, chdir_name);
X free (path);
X }
X }
X
X if (name)
X {
X i = strlen (name);
X /*NOSTRICT*/
X p = (struct name *) malloc ((unsigned) (sizeof (struct name) + i));
X }
X else
X p = (struct name *) malloc ((unsigned) (sizeof (struct name)));
X if (!p)
X {
X if (name)
X msg ("cannot allocate mem for name '%s'.", name);
X else
X msg ("cannot allocate mem for chdir record.");
X exit (EX_SYSTEM);
X }
X p->next = (struct name *) NULL;
X if (name)
X {
X p->fake = 0;
X p->length = i;
X strncpy (p->name, name, i);
X p->name[i] = '\0'; /* Null term */
X }
X else
X p->fake = 1;
X p->found = 0;
X p->regexp = 0; /* Assume not a regular expression */
X p->firstch = 1; /* Assume first char is literal */
X p->change_dir = chdir_name;
X p->dir_contents = 0; /* JF */
X if (name)
X {
X if (index (name, '*') || index (name, '[') || index (name, '?'))
X {
X p->regexp = 1; /* No, it's a regexp */
X if (name[0] == '*' || name[0] == '[' || name[0] == '?')
X p->firstch = 0; /* Not even 1st char literal */
X }
X }
X
X if (namelast)
X namelast->next = p;
X namelast = p;
X if (!namelist)
X namelist = p;
X}
X
X/*
X * Return nonzero if name P (from an archive) matches any name from
X * the namelist, zero if not.
X */
Xint
Xname_match (p)
X register char *p;
X{
X register struct name *nlp;
X register int len;
X
Xagain:
X if (0 == (nlp = namelist)) /* Empty namelist is easy */
X return 1;
X if (nlp->fake)
X {
X if (nlp->change_dir && chdir (nlp->change_dir))
X msg_perror ("Can't change to directory %d", nlp->change_dir);
X namelist = 0;
X return 1;
X }
X len = strlen (p);
X for (; nlp != 0; nlp = nlp->next)
X {
X /* If first chars don't match, quick skip */
X if (nlp->firstch && nlp->name[0] != p[0])
X continue;
X
X /* Regular expressions (shell globbing, actually). */
X if (nlp->regexp)
X {
X if (fnmatch (nlp->name, p, FNM_LEADING_DIR) == 0)
X {
X nlp->found = 1; /* Remember it matched */
X if (f_startfile)
X {
X free ((void *) namelist);
X namelist = 0;
X }
X if (nlp->change_dir && chdir (nlp->change_dir))
X msg_perror ("Can't change to directory %s", nlp->change_dir);
X return 1; /* We got a match */
X }
X continue;
X }
X
X /* Plain Old Strings */
X if (nlp->length <= len /* Archive len >= specified */
X && (p[nlp->length] == '\0' || p[nlp->length] == '/')
X /* Full match on file/dirname */
X && strncmp (p, nlp->name, nlp->length) == 0) /* Name compare */
X {
X nlp->found = 1; /* Remember it matched */
X if (f_startfile)
X {
X free ((void *) namelist);
X namelist = 0;
X }
X if (nlp->change_dir && chdir (nlp->change_dir))
X msg_perror ("Can't change to directory %s", nlp->change_dir);
X return 1; /* We got a match */
X }
X }
X
X /*
X * Filename from archive not found in namelist.
X * If we have the whole namelist here, just return 0.
X * Otherwise, read the next name in and compare it.
X * If this was the last name, namelist->found will remain on.
X * If not, we loop to compare the newly read name.
X */
X if (f_sorted_names && namelist->found)
X {
X name_gather (); /* Read one more */
X if (!namelist->found)
X goto again;
X }
X return 0;
X}
X
X
X/*
X * Print the names of things in the namelist that were not matched.
X */
Xvoid
Xnames_notfound ()
X{
X register struct name *nlp, *next;
X register char *p;
X
X for (nlp = namelist; nlp != 0; nlp = next)
X {
X next = nlp->next;
X if (!nlp->found)
X msg ("%s not found in archive", nlp->name);
X
X /*
X * We could free() the list, but the process is about
X * to die anyway, so save some CPU time. Amigas and
X * other similarly broken software will need to waste
X * the time, though.
X */
X#ifdef amiga
X if (!f_sorted_names)
X free (nlp);
X#endif
X }
X namelist = (struct name *) NULL;
X namelast = (struct name *) NULL;
X
X if (f_sorted_names)
X {
X while (0 != (p = name_next (1)))
X msg ("%s not found in archive", p);
X }
X}
X
X/* These next routines were created by JF */
X
Xvoid
Xname_expand ()
X{
X ;
X}
X
X/* This is like name_match(), except that it returns a pointer to the name
X it matched, and doesn't set ->found The caller will have to do that
X if it wants to. Oh, and if the namelist is empty, it returns 0, unlike
X name_match(), which returns TRUE */
X
Xstruct name *
Xname_scan (p)
X register char *p;
X{
X register struct name *nlp;
X register int len;
X
Xagain:
X if (0 == (nlp = namelist)) /* Empty namelist is easy */
X return 0;
X len = strlen (p);
X for (; nlp != 0; nlp = nlp->next)
X {
X /* If first chars don't match, quick skip */
X if (nlp->firstch && nlp->name[0] != p[0])
X continue;
X
X /* Regular expressions */
X if (nlp->regexp)
X {
X if (fnmatch (nlp->name, p, FNM_LEADING_DIR) == 0)
X return nlp; /* We got a match */
X continue;
X }
X
X /* Plain Old Strings */
X if (nlp->length <= len /* Archive len >= specified */
X && (p[nlp->length] == '\0' || p[nlp->length] == '/')
X /* Full match on file/dirname */
X && strncmp (p, nlp->name, nlp->length) == 0) /* Name compare */
X return nlp; /* We got a match */
X }
X
X /*
X * Filename from archive not found in namelist.
X * If we have the whole namelist here, just return 0.
X * Otherwise, read the next name in and compare it.
X * If this was the last name, namelist->found will remain on.
X * If not, we loop to compare the newly read name.
X */
X if (f_sorted_names && namelist->found)
X {
X name_gather (); /* Read one more */
X if (!namelist->found)
X goto again;
X }
X return (struct name *) 0;
X}
X
X/* This returns a name from the namelist which doesn't have ->found set.
X It sets ->found before returning, so successive calls will find and return
X all the non-found names in the namelist */
X
Xstruct name *gnu_list_name;
X
Xchar *
Xname_from_list ()
X{
X if (!gnu_list_name)
X gnu_list_name = namelist;
X while (gnu_list_name && gnu_list_name->found)
X gnu_list_name = gnu_list_name->next;
X if (gnu_list_name)
X {
X gnu_list_name->found++;
X if (gnu_list_name->change_dir)
X if (chdir (gnu_list_name->change_dir) < 0)
X msg_perror ("can't chdir to %s", gnu_list_name->change_dir);
X return gnu_list_name->name;
X }
X return (char *) 0;
X}
X
Xvoid
Xblank_name_list ()
X{
X struct name *n;
X
X gnu_list_name = 0;
X for (n = namelist; n; n = n->next)
X n->found = 0;
X}
X
Xchar *
Xnew_name (path, name)
X char *path, *name;
X{
X char *path_buf;
X
X path_buf = (char *) malloc (strlen (path) + strlen (name) + 2);
X if (path_buf == 0)
X {
X msg ("Can't allocate memory for name '%s/%s", path, name);
X exit (EX_SYSTEM);
X }
X (void) sprintf (path_buf, "%s/%s", path, name);
X return path_buf;
X}
X
X/* returns non-zero if the luser typed 'y' or 'Y', zero otherwise. */
X
Xint
Xconfirm (action, file)
X char *action, *file;
X{
X int c, nl;
X static FILE *confirm_file = 0;
X extern FILE *msg_file;
X extern char TTY_NAME[];
X
X fprintf (msg_file, "%s %s?", action, file);
X fflush (msg_file);
X if (!confirm_file)
X {
X confirm_file = (archive == 0) ? fopen (TTY_NAME, "r") : stdin;
X if (!confirm_file)
X {
X msg ("Can't read confirmation from user");
X exit (EX_SYSTEM);
X }
X }
X c = getc (confirm_file);
X for (nl = c; nl != '\n' && nl != EOF; nl = getc (confirm_file))
X ;
X return (c == 'y' || c == 'Y');
X}
X
Xchar *x_buffer = 0;
Xint size_x_buffer;
Xint free_x_buffer;
X
Xchar **exclude = 0;
Xint size_exclude = 0;
Xint free_exclude = 0;
X
Xchar **re_exclude = 0;
Xint size_re_exclude = 0;
Xint free_re_exclude = 0;
X
Xvoid
Xadd_exclude (name)
X char *name;
X{
X /* char *rname;*/
X /* char **tmp_ptr;*/
X int size_buf;
X
X un_quote_string (name);
X size_buf = strlen (name);
X
X if (x_buffer == 0)
X {
X x_buffer = (char *) ck_malloc (size_buf + 1024);
X free_x_buffer = 1024;
X }
X else if (free_x_buffer <= size_buf)
X {
X char *old_x_buffer;
X char **tmp_ptr;
X
X old_x_buffer = x_buffer;
X x_buffer = (char *) ck_realloc (x_buffer, size_x_buffer + 1024);
X free_x_buffer = 1024;
X for (tmp_ptr = exclude; tmp_ptr < exclude + size_exclude; tmp_ptr++)
X *tmp_ptr = x_buffer + ((*tmp_ptr) - old_x_buffer);
X for (tmp_ptr = re_exclude; tmp_ptr < re_exclude + size_re_exclude; tmp_ptr++)
X *tmp_ptr = x_buffer + ((*tmp_ptr) - old_x_buffer);
X }
X
X if (is_regex (name))
X {
X if (free_re_exclude == 0)
X {
X re_exclude = (char **) (re_exclude ? ck_realloc (re_exclude, (size_re_exclude + 32) * sizeof (char *)): ck_malloc (sizeof (char *) * 32));
X free_re_exclude += 32;
X }
X re_exclude[size_re_exclude] = x_buffer + size_x_buffer;
X size_re_exclude++;
X free_re_exclude--;
X }
X else
X {
X if (free_exclude == 0)
X {
X exclude = (char **) (exclude ? ck_realloc (exclude, (size_exclude + 32) * sizeof (char *)): ck_malloc (sizeof (char *) * 32));
X free_exclude += 32;
X }
X exclude[size_exclude] = x_buffer + size_x_buffer;
X size_exclude++;
X free_exclude--;
X }
X strcpy (x_buffer + size_x_buffer, name);
X size_x_buffer += size_buf + 1;
X free_x_buffer -= size_buf + 1;
X}
X
Xvoid
Xadd_exclude_file (file)
X char *file;
X{
X FILE *fp;
X char buf[1024];
X
X if (strcmp (file, "-"))
X fp = fopen (file, "r");
X else
X /* Let's hope the person knows what they're doing. */
X /* Using -X - -T - -f - will get you *REALLY* strange
X results. . . */
X fp = stdin;
X
X if (!fp)
X {
X msg_perror ("can't open %s", file);
X exit (2);
X }
X while (fgets (buf, 1024, fp))
X {
X /* int size_buf;*/
X char *end_str;
X
X end_str = rindex (buf, '\n');
X if (end_str)
X *end_str = '\0';
X add_exclude (buf);
X
X }
X fclose (fp);
X}
X
Xint
Xis_regex (str)
X char *str;
X{
X return index (str, '*') || index (str, '[') || index (str, '?');
X}
X
X/* Returns non-zero if the file 'name' should not be added/extracted */
Xint
Xcheck_exclude (name)
X char *name;
X{
X int n;
X char *str;
X extern char *strstr ();
X
X for (n = 0; n < size_re_exclude; n++)
X {
X if (fnmatch (re_exclude[n], name, FNM_LEADING_DIR) == 0)
X return 1;
X }
X for (n = 0; n < size_exclude; n++)
X {
X /* Accept the output from strstr only if it is the last
X part of the string. There is certainly a faster way to
X do this. . . */
X if ((str = strstr (name, exclude[n]))
X && (str == name || str[-1] == '/')
X && str[strlen (exclude[n])] == '\0')
X return 1;
X }
X return 0;
X}
END_OF_FILE
if test 36191 -ne `wc -c <'tar.c'`; then
echo shar: \"'tar.c'\" unpacked with wrong size!
fi
# end of 'tar.c'
fi
if test -f 'create.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'create.c'\"
else
echo shar: Extracting \"'create.c'\" \(33888 characters\)
sed "s/^X//" >'create.c' <<'END_OF_FILE'
X/* Create a tar archive.
X Copyright (C) 1985, 1992, 1993 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/*
X * Create a tar archive.
X *
X * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
X */
X
X#ifdef _AIX
X #pragma alloca
X#endif
X#include <sys/types.h>
X#include <stdio.h>
X#include <errno.h>
X#ifndef STDC_HEADERS
Xextern int errno;
X#endif
X
X#ifdef BSD42
X#include <sys/file.h>
X#else
X#ifndef V7
X#include <fcntl.h>
X#endif
X#endif
X
X#include "tar.h"
X#include "port.h"
X
X#ifndef __MSDOS__
X#include <pwd.h>
X#include <grp.h>
X#endif
X
X#if defined (_POSIX_VERSION)
X#include <utime.h>
X#else
Xstruct utimbuf
X{
X long actime;
X long modtime;
X};
X
X#endif
X
Xextern struct stat hstat; /* Stat struct corresponding */
X
X#ifndef __MSDOS__
Xextern dev_t ar_dev;
Xextern ino_t ar_ino;
X#endif
X
X/* JF */
Xextern struct name *gnu_list_name;
X
X/*
X * If there are no symbolic links, there is no lstat(). Use stat().
X */
X#ifndef S_ISLNK
X#define lstat stat
X#endif
X
Xextern void print_header ();
X
Xunion record *start_header ();
Xvoid blank_name_list ();
Xint check_exclude ();
XPTR ck_malloc ();
XPTR ck_realloc ();
Xvoid clear_buffer ();
Xvoid close_archive ();
Xvoid collect_and_sort_names ();
Xint confirm ();
Xint deal_with_sparse ();
Xvoid find_new_file_size ();
Xvoid finish_header ();
Xint finish_sparse_file ();
Xvoid finduname ();
Xvoid findgname ();
Xint is_dot_or_dotdot ();
Xvoid open_archive ();
Xchar *name_next ();
Xvoid name_close ();
Xvoid to_oct ();
Xvoid dump_file ();
Xvoid write_dir_file ();
Xvoid write_eot ();
Xvoid write_long ();
Xint zero_record ();
X
X/* This code moved from tar.h since create.c is the only file that cares
X about 'struct link's. This means that other files might not have to
X include sys/types.h any more. */
X
Xstruct link
X {
X struct link *next;
X dev_t dev;
X ino_t ino;
X short linkcount;
X char name[1];
X };
X
Xstruct link *linklist; /* Points to first link in list */
X
Xstatic nolinks; /* Gets set if we run out of RAM */
X
X/*
X * "Scratch" space to store the information about a sparse file before
X * writing the info into the header or extended header
X */
X/* struct sp_array *sparsearray;*/
X
X/* number of elts storable in the sparsearray */
X/*int sparse_array_size = 10;*/
X
Xvoid
Xcreate_archive ()
X{
X register char *p;
X char *name_from_list ();
X
X open_archive (0); /* Open for writing */
X
X if (f_gnudump)
X {
X char *buf = ck_malloc (PATH_MAX);
X char *q, *bufp;
X
X collect_and_sort_names ();
X
X while (p = name_from_list ())
X dump_file (p, -1, 1);
X /* if(!f_dironly) { */
X blank_name_list ();
X while (p = name_from_list ())
X {
X strcpy (buf, p);
X if (p[strlen (p) - 1] != '/')
X strcat (buf, "/");
X bufp = buf + strlen (buf);
X for (q = gnu_list_name->dir_contents; q && *q; q += strlen (q) + 1)
X {
X if (*q == 'Y')
X {
X strcpy (bufp, q + 1);
X dump_file (buf, -1, 1);
X }
X }
X }
X /* } */
X free (buf);
X }
X else
X {
X while (p = name_next (1))
X dump_file (p, -1, 1);
X }
X
X write_eot ();
X close_archive ();
X if (f_gnudump)
X write_dir_file ();
X name_close ();
X}
X
X/*
X * Dump a single file. If it's a directory, recurse.
X * Result is 1 for success, 0 for failure.
X * Sets global "hstat" to stat() output for this file.
X */
Xvoid
Xdump_file (p, curdev, toplevel)
X char *p; /* File name to dump */
X int curdev; /* Device our parent dir was on */
X int toplevel; /* Whether we are a toplevel call */
X{
X union record *header;
X char type;
X extern char *save_name; /* JF for multi-volume support */
X extern long save_totsize;
X extern long save_sizeleft;
X union record *exhdr;
X char save_linkflag;
X extern time_t new_time;
X int critical_error = 0;
X struct utimbuf restore_times;
X /* int sparse_ind = 0;*/
X
X
X if (f_confirm && !confirm ("add", p))
X return;
X
X /*
X * Use stat if following (rather than dumping) 4.2BSD's
X * symbolic links. Otherwise, use lstat (which, on non-4.2
X * systems, is #define'd to stat anyway.
X */
X#ifdef STX_HIDDEN /* AIX */
X if (0 != f_follow_links ?
X statx (p, &hstat, STATSIZE, STX_HIDDEN) :
X statx (p, &hstat, STATSIZE, STX_HIDDEN | STX_LINK))
X#else
X if (0 != f_follow_links ? stat (p, &hstat) : lstat (p, &hstat))
X#endif
X {
X badperror:
X msg_perror ("can't add file %s", p);
X badfile:
X if (!f_ignore_failed_read || critical_error)
X errors++;
X return;
X }
X
X restore_times.actime = hstat.st_atime;
X restore_times.modtime = hstat.st_mtime;
X
X#ifdef S_ISHIDDEN
X if (S_ISHIDDEN (hstat.st_mode))
X {
X char *new = (char *) alloca (strlen (p) + 2);
X if (new)
X {
X strcpy (new, p);
X strcat (new, "@");
X p = new;
X }
X }
X#endif
X
X /* See if we only want new files, and check if this one is too old to
X put in the archive. */
X if (f_new_files
X && !f_gnudump
X && new_time > hstat.st_mtime
X && !S_ISDIR (hstat.st_mode)
X && (f_new_files > 1 || new_time > hstat.st_ctime))
X {
X if (curdev == -1)
X {
X msg ("%s: is unchanged; not dumped", p);
X }
X return;
X }
X
X#ifndef __MSDOS__
X /* See if we are trying to dump the archive */
X if (ar_dev && hstat.st_dev == ar_dev && hstat.st_ino == ar_ino)
X {
X msg ("%s is the archive; not dumped", p);
X return;
X }
X#endif
X /*
X * Check for multiple links.
X *
X * We maintain a list of all such files that we've written so
X * far. Any time we see another, we check the list and
X * avoid dumping the data again if we've done it once already.
X */
X if (hstat.st_nlink > 1
X && (S_ISREG (hstat.st_mode)
X#ifdef S_ISCTG
X || S_ISCTG (hstat.st_mode)
X#endif
X#ifdef S_ISCHR
X || S_ISCHR (hstat.st_mode)
X#endif
X#ifdef S_ISBLK
X || S_ISBLK (hstat.st_mode)
X#endif
X#ifdef S_ISFIFO
X || S_ISFIFO (hstat.st_mode)
X#endif
X ))
X {
X register struct link *lp;
X
X /* First quick and dirty. Hashing, etc later FIXME */
X for (lp = linklist; lp; lp = lp->next)
X {
X if (lp->ino == hstat.st_ino &&
X lp->dev == hstat.st_dev)
X {
X char *link_name = lp->name;
X
X /* We found a link. */
X while (!f_absolute_paths && *link_name == '/')
X {
X static int link_warn = 0;
X
X if (!link_warn)
X {
X msg ("Removing leading / from absolute links");
X link_warn++;
X }
X link_name++;
X }
X if (link_name - lp->name >= NAMSIZ)
X write_long (link_name, LF_LONGLINK);
X current_link_name = link_name;
X
X hstat.st_size = 0;
X header = start_header (p, &hstat);
X if (header == NULL)
X {
X critical_error = 1;
X goto badfile;
X }
X strncpy (header->header.arch_linkname,
X link_name, NAMSIZ);
X
X /* Force null truncated */
X header->header.arch_linkname[NAMSIZ - 1] = 0;
X
X header->header.linkflag = LF_LINK;
X finish_header (header);
X /* FIXME: Maybe remove from list after all links found? */
X if (f_remove_files)
X {
X if (unlink (p) == -1)
X msg_perror ("cannot remove %s", p);
X }
X return; /* We dumped it */
X }
X }
X
X /* Not found. Add it to the list of possible links. */
X lp = (struct link *) ck_malloc ((unsigned) (sizeof (struct link) + strlen (p)));
X if (!lp)
X {
X if (!nolinks)
X {
X msg (
X "no memory for links, they will be dumped as separate files");
X nolinks++;
X }
X }
X lp->ino = hstat.st_ino;
X lp->dev = hstat.st_dev;
X strcpy (lp->name, p);
X lp->next = linklist;
X linklist = lp;
X }
X
X /*
X * This is not a link to a previously dumped file, so dump it.
X */
X if (S_ISREG (hstat.st_mode)
X#ifdef S_ISCTG
X || S_ISCTG (hstat.st_mode)
X#endif
X )
X {
X int f; /* File descriptor */
X long bufsize, count;
X long sizeleft;
X register union record *start;
X int header_moved;
X char isextended = 0;
X int upperbound;
X /* int end_nulls = 0; */
X
X header_moved = 0;
X
X#ifdef BSD42
X if (f_sparse_files)
X {
X /*
X * JK - This is the test for sparseness: whether the
X * "size" of the file matches the number of blocks
X * allocated for it. If there is a smaller number
X * of blocks that would be necessary to accommodate
X * a file of this size, we have a sparse file, i.e.,
X * at least one of those records in the file is just
X * a useless hole.
X */
X#ifdef hpux /* Nice of HPUX to gratuitiously change it, huh? - mib */
X if (hstat.st_size - (hstat.st_blocks * 1024) > 1024)
X#else
X if (hstat.st_size - (hstat.st_blocks * RECORDSIZE) > RECORDSIZE)
X#endif
X {
X int filesize = hstat.st_size;
X register int i;
X
X header = start_header (p, &hstat);
X if (header == NULL)
X {
X critical_error = 1;
X goto badfile;
X }
X header->header.linkflag = LF_SPARSE;
X header_moved++;
X
X /*
X * Call the routine that figures out the
X * layout of the sparse file in question.
X * UPPERBOUND is the index of the last
X * element of the "sparsearray," i.e.,
X * the number of elements it needed to
X * describe the file.
X */
X
X upperbound = deal_with_sparse (p, header);
X
X /*
X * See if we'll need an extended header
X * later
X */
X if (upperbound > SPARSE_IN_HDR - 1)
X header->header.isextended++;
X /*
X * We store the "real" file size so
X * we can show that in case someone wants
X * to list the archive, i.e., tar tvf <file>.
X * It might be kind of disconcerting if the
X * shrunken file size was the one that showed
X * up.
X */
X to_oct ((long) hstat.st_size, 1 + 12,
X header->header.realsize);
X
X /*
X * This will be the new "size" of the
X * file, i.e., the size of the file
X * minus the records of holes that we're
X * skipping over.
X */
X
X find_new_file_size (&filesize, upperbound);
X hstat.st_size = filesize;
X to_oct ((long) filesize, 1 + 12,
X header->header.size);
X /* to_oct((long) end_nulls, 1+12,
X header->header.ending_blanks);*/
X
X for (i = 0; i < SPARSE_IN_HDR; i++)
X {
X if (!sparsearray[i].numbytes)
X break;
X to_oct (sparsearray[i].offset, 1 + 12,
X header->header.sp[i].offset);
X to_oct (sparsearray[i].numbytes, 1 + 12,
X header->header.sp[i].numbytes);
X }
X
X }
X }
X#else
X upperbound = SPARSE_IN_HDR - 1;
X#endif
X
X sizeleft = hstat.st_size;
X /* Don't bother opening empty, world readable files. */
X if (sizeleft > 0 || 0444 != (0444 & hstat.st_mode))
X {
X f = open (p, O_RDONLY | O_BINARY);
X if (f < 0)
X goto badperror;
X }
X else
X {
X f = -1;
X }
X
X /* If the file is sparse, we've already taken care of this */
X if (!header_moved)
X {
X header = start_header (p, &hstat);
X if (header == NULL)
X {
X if (f >= 0)
X (void) close (f);
X critical_error = 1;
X goto badfile;
X }
X }
X#ifdef S_ISCTG
X /* Mark contiguous files, if we support them */
X if (f_standard && S_ISCTG (hstat.st_mode))
X {
X header->header.linkflag = LF_CONTIG;
X }
X#endif
X isextended = header->header.isextended;
X save_linkflag = header->header.linkflag;
X finish_header (header);
X if (isextended)
X {
X /* int sum = 0;*/
X register int i;
X /* register union record *exhdr;*/
X /* int arraybound = SPARSE_EXT_HDR;*/
X /* static */ int index_offset = SPARSE_IN_HDR;
X
X extend:exhdr = findrec ();
X
X if (exhdr == NULL)
X {
X critical_error = 1;
X goto badfile;
X }
X bzero (exhdr->charptr, RECORDSIZE);
X for (i = 0; i < SPARSE_EXT_HDR; i++)
X {
X if (i + index_offset > upperbound)
X break;
X to_oct ((long) sparsearray[i + index_offset].numbytes,
X 1 + 12,
X exhdr->ext_hdr.sp[i].numbytes);
X to_oct ((long) sparsearray[i + index_offset].offset,
X 1 + 12,
X exhdr->ext_hdr.sp[i].offset);
X }
X userec (exhdr);
X /* sum += i;
X if (sum < upperbound)
X goto extend;*/
X if (index_offset + i <= upperbound)
X {
X index_offset += i;
X exhdr->ext_hdr.isextended++;
X goto extend;
X }
X
X }
X if (save_linkflag == LF_SPARSE)
X {
X if (finish_sparse_file (f, &sizeleft, hstat.st_size, p))
X goto padit;
X }
X else
X while (sizeleft > 0)
X {
X
X if (f_multivol)
X {
X save_name = p;
X save_sizeleft = sizeleft;
X save_totsize = hstat.st_size;
X }
X start = findrec ();
X
X bufsize = endofrecs ()->charptr - start->charptr;
X
X if (sizeleft < bufsize)
X {
X /* Last read -- zero out area beyond */
X bufsize = (int) sizeleft;
X count = bufsize % RECORDSIZE;
X if (count)
X bzero (start->charptr + sizeleft,
X (int) (RECORDSIZE - count));
X }
X count = read (f, start->charptr, bufsize);
X if (count < 0)
X {
X msg_perror ("read error at byte %ld, reading\
X %d bytes, in file %s", hstat.st_size - sizeleft, bufsize, p);
X goto padit;
X }
X sizeleft -= count;
X
X /* This is nonportable (the type of userec's arg). */
X userec (start + (count - 1) / RECORDSIZE);
X
X if (count == bufsize)
X continue;
X msg ("file %s shrunk by %d bytes, padding with zeros.", p, sizeleft);
X goto padit; /* Short read */
X }
X
X if (f_multivol)
X save_name = 0;
X
X if (f >= 0)
X (void) close (f);
X
X if (f_remove_files)
X {
X if (unlink (p) == -1)
X msg_perror ("cannot remove %s", p);
X }
X if (f_atime_preserve)
X utime (p, &restore_times);
X return;
X
X /*
X * File shrunk or gave error, pad out tape to match
X * the size we specified in the header.
X */
X padit:
X while (sizeleft > 0)
X {
X save_sizeleft = sizeleft;
X start = findrec ();
X bzero (start->charptr, RECORDSIZE);
X userec (start);
X sizeleft -= RECORDSIZE;
X }
X if (f_multivol)
X save_name = 0;
X if (f >= 0)
X (void) close (f);
X if (f_atime_preserve)
X utime (p, &restore_times);
X return;
X }
X
X#ifdef S_ISLNK
X else if (S_ISLNK (hstat.st_mode))
X {
X int size;
X char *buf = alloca (PATH_MAX + 1);
X
X size = readlink (p, buf, PATH_MAX + 1);
X if (size < 0)
X goto badperror;
X buf[size] = '\0';
X if (size >= NAMSIZ)
X write_long (buf, LF_LONGLINK);
X current_link_name = buf;
X
X hstat.st_size = 0; /* Force 0 size on symlink */
X header = start_header (p, &hstat);
X if (header == NULL)
X {
X critical_error = 1;
X goto badfile;
X }
X strncpy (header->header.arch_linkname, buf, NAMSIZ);
X header->header.arch_linkname[NAMSIZ - 1] = '\0';
X header->header.linkflag = LF_SYMLINK;
X finish_header (header); /* Nothing more to do to it */
X if (f_remove_files)
X {
X if (unlink (p) == -1)
X msg_perror ("cannot remove %s", p);
X }
X return;
X }
X#endif
X
X else if (S_ISDIR (hstat.st_mode))
X {
X register DIR *dirp;
X register struct dirent *d;
X char *namebuf;
X int buflen;
X register int len;
X int our_device = hstat.st_dev;
X
X /* Build new prototype name */
X len = strlen (p);
X buflen = len + NAMSIZ;
X namebuf = ck_malloc (buflen + 1);
X strncpy (namebuf, p, buflen);
X while (len >= 1 && '/' == namebuf[len - 1])
X len--; /* Delete trailing slashes */
X namebuf[len++] = '/'; /* Now add exactly one back */
X namebuf[len] = '\0'; /* Make sure null-terminated */
X
X /*
X * Output directory header record with permissions
X * FIXME, do this AFTER files, to avoid R/O dir problems?
X * If old archive format, don't write record at all.
X */
X if (!f_oldarch)
X {
X hstat.st_size = 0; /* Force 0 size on dir */
X /*
X * If people could really read standard archives,
X * this should be: (FIXME)
X header = start_header(f_standard? p: namebuf, &hstat);
X * but since they'd interpret LF_DIR records as
X * regular files, we'd better put the / on the name.
X */
X header = start_header (namebuf, &hstat);
X if (header == NULL)
X {
X critical_error = 1;
X goto badfile; /* eg name too long */
X }
X
X if (f_gnudump)
X header->header.linkflag = LF_DUMPDIR;
X else if (f_standard)
X header->header.linkflag = LF_DIR;
X
X /* If we're gnudumping, we aren't done yet so don't close it. */
X if (!f_gnudump)
X finish_header (header); /* Done with directory header */
X }
X
X if (f_gnudump)
X {
X int sizeleft;
X int totsize;
X int bufsize;
X union record *start;
X int count;
X char *buf, *p_buf;
X
X buf = gnu_list_name->dir_contents; /* FOO */
X totsize = 0;
X for (p_buf = buf; p_buf && *p_buf;)
X {
X int tmp;
X
X tmp = strlen (p_buf) + 1;
X totsize += tmp;
X p_buf += tmp;
X }
X totsize++;
X to_oct ((long) totsize, 1 + 12, header->header.size);
X finish_header (header);
X p_buf = buf;
X sizeleft = totsize;
X while (sizeleft > 0)
X {
X if (f_multivol)
X {
X save_name = p;
X save_sizeleft = sizeleft;
X save_totsize = totsize;
X }
X start = findrec ();
X bufsize = endofrecs ()->charptr - start->charptr;
X if (sizeleft < bufsize)
X {
X bufsize = sizeleft;
X count = bufsize % RECORDSIZE;
X if (count)
X bzero (start->charptr + sizeleft, RECORDSIZE - count);
X }
X bcopy (p_buf, start->charptr, bufsize);
X sizeleft -= bufsize;
X p_buf += bufsize;
X userec (start + (bufsize - 1) / RECORDSIZE);
X }
X if (f_multivol)
X save_name = 0;
X if (f_atime_preserve)
X utime (p, &restore_times);
X return;
X }
X
X /* Now output all the files in the directory */
X#if 0
X if (f_dironly)
X return; /* Unless the cmdline said not to */
X#endif
X /*
X * See if we are crossing from one file system to another,
X * and avoid doing so if the user only wants to dump one file system.
X */
X if (f_local_filesys && !toplevel && curdev != hstat.st_dev)
X {
X if (f_verbose)
X msg ("%s: is on a different filesystem; not dumped", p);
X return;
X }
X
X
X errno = 0;
X dirp = opendir (p);
X if (!dirp)
X {
X if (errno)
X {
X msg_perror ("can't open directory %s", p);
X }
X else
X {
X msg ("error opening directory %s",
X p);
X }
X return;
X }
X
X /* Hack to remove "./" from the front of all the file names */
X if (len == 2 && namebuf[0] == '.' && namebuf[1] == '/')
X len = 0;
X
X /* Should speed this up by cd-ing into the dir, FIXME */
X while (NULL != (d = readdir (dirp)))
X {
X /* Skip . and .. */
X if (is_dot_or_dotdot (d->d_name))
X continue;
X
X if (NLENGTH (d) + len >= buflen)
X {
X buflen = len + NLENGTH (d);
X namebuf = ck_realloc (namebuf, buflen + 1);
X /* namebuf[len]='\0';
X msg("file name %s%s too long",
X namebuf, d->d_name);
X continue; */
X }
X strcpy (namebuf + len, d->d_name);
X if (f_exclude && check_exclude (namebuf))
X continue;
X dump_file (namebuf, our_device, 0);
X }
X
X closedir (dirp);
X free (namebuf);
X if (f_atime_preserve)
X utime (p, &restore_times);
X return;
X }
X
X#ifdef S_ISCHR
X else if (S_ISCHR (hstat.st_mode))
X {
X type = LF_CHR;
X }
X#endif
X
X#ifdef S_ISBLK
X else if (S_ISBLK (hstat.st_mode))
X {
X type = LF_BLK;
X }
X#endif
X
X /* Avoid screwy apollo lossage where S_IFIFO == S_IFSOCK */
X#if (_ISP__M68K == 0) && (_ISP__A88K == 0) && defined(S_ISFIFO)
X else if (S_ISFIFO (hstat.st_mode))
X {
X type = LF_FIFO;
X }
X#endif
X
X#ifdef S_ISSOCK
X else if (S_ISSOCK (hstat.st_mode))
X {
X type = LF_FIFO;
X }
X#endif
X else
X goto unknown;
X
X if (!f_standard)
X goto unknown;
X
X hstat.st_size = 0; /* Force 0 size */
X header = start_header (p, &hstat);
X if (header == NULL)
X {
X critical_error = 1;
X goto badfile; /* eg name too long */
X }
X
X header->header.linkflag = type;
X#if defined(S_IFBLK) || defined(S_IFCHR)
X if (type != LF_FIFO)
X {
X to_oct ((long) major (hstat.st_rdev), 8,
X header->header.devmajor);
X to_oct ((long) minor (hstat.st_rdev), 8,
X header->header.devminor);
X }
X#endif
X
X finish_header (header);
X if (f_remove_files)
X {
X if (unlink (p) == -1)
X msg_perror ("cannot remove %s", p);
X }
X return;
X
Xunknown:
X msg ("%s: Unknown file type; file ignored.", p);
X}
X
Xint
Xfinish_sparse_file (fd, sizeleft, fullsize, name)
X int fd;
X long *sizeleft, fullsize;
X char *name;
X{
X union record *start;
X char tempbuf[RECORDSIZE];
X int bufsize, sparse_ind = 0, count;
X long pos;
X long nwritten = 0;
X
X
X while (*sizeleft > 0)
X {
X start = findrec ();
X bzero (start->charptr, RECORDSIZE);
X bufsize = sparsearray[sparse_ind].numbytes;
X if (!bufsize)
X { /* we blew it, maybe */
X msg ("Wrote %ld of %ld bytes to file %s",
X fullsize - *sizeleft, fullsize, name);
X break;
X }
X pos = lseek (fd, sparsearray[sparse_ind++].offset, 0);
X /*
X * If the number of bytes to be written here exceeds
X * the size of the temporary buffer, do it in steps.
X */
X while (bufsize > RECORDSIZE)
X {
X /* if (amt_read) {
X count = read(fd, start->charptr+amt_read, RECORDSIZE-amt_read);
X bufsize -= RECORDSIZE - amt_read;
X amt_read = 0;
X userec(start);
X start = findrec();
X bzero(start->charptr, RECORDSIZE);
X }*/
X /* store the data */
X count = read (fd, start->charptr, RECORDSIZE);
X if (count < 0)
X {
X msg_perror ("read error at byte %ld, reading %d bytes, in file %s",
X fullsize - *sizeleft, bufsize, name);
X return 1;
X }
X bufsize -= count;
X *sizeleft -= count;
X userec (start);
X nwritten += RECORDSIZE; /* XXX */
X start = findrec ();
X bzero (start->charptr, RECORDSIZE);
X }
X
X
X clear_buffer (tempbuf);
X count = read (fd, tempbuf, bufsize);
X bcopy (tempbuf, start->charptr, RECORDSIZE);
X if (count < 0)
X {
X msg_perror ("read error at byte %ld, reading %d bytes, in file %s",
X fullsize - *sizeleft, bufsize, name);
X return 1;
X }
X /* if (amt_read >= RECORDSIZE) {
X amt_read = 0;
X userec(start+(count-1)/RECORDSIZE);
X if (count != bufsize) {
X msg("file %s shrunk by %d bytes, padding with zeros.", name, sizeleft);
X return 1;
X }
X start = findrec();
X } else
X amt_read += bufsize;*/
X nwritten += count; /* XXX */
X *sizeleft -= count;
X userec (start);
X
X }
X free (sparsearray);
X /* printf ("Amount actually written is (I hope) %d.\n", nwritten); */
X /* userec(start+(count-1)/RECORDSIZE);*/
X return 0;
X
X}
X
Xvoid
Xinit_sparsearray ()
X{
X register int i;
X
X sp_array_size = 10;
X /*
X * Make room for our scratch space -- initially is 10 elts long
X */
X sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
X for (i = 0; i < sp_array_size; i++)
X {
X sparsearray[i].offset = 0;
X sparsearray[i].numbytes = 0;
X }
X}
X
X
X
X/*
X * Okay, we've got a sparse file on our hands -- now, what we need to do is
X * make a pass through the file and carefully note where any data is, i.e.,
X * we want to find how far into the file each instance of data is, and how
X * many bytes are there. We store this information in the sparsearray,
X * which will later be translated into header information. For now, we use
X * the sparsearray as convenient storage.
X *
X * As a side note, this routine is a mess. If I could have found a cleaner
X * way to do it, I would have. If anyone wants to find a nicer way to do
X * this, feel free.
X */
X
X/* There is little point in trimming small amounts of null data at the */
X/* head and tail of blocks -- it's ok if we only avoid dumping blocks */
X/* of complete null data */
Xint
Xdeal_with_sparse (name, header, nulls_at_end)
X char *name;
X union record *header;
X int nulls_at_end;
X{
X long numbytes = 0;
X long offset = 0;
X /* long save_offset;*/
X int fd;
X /* int current_size = hstat.st_size;*/
X int sparse_ind = 0, cc;
X char buf[RECORDSIZE];
X#if 0
X int read_last_data = 0; /* did we just read the last record? */
X#endif
X int amidst_data = 0;
X
X header->header.isextended = 0;
X /*
X * Can't open the file -- this problem will be caught later on,
X * so just return.
X */
X if ((fd = open (name, O_RDONLY)) < 0)
X return 0;
X
X init_sparsearray ();
X clear_buffer (buf);
X
X while ((cc = read (fd, buf, sizeof buf)) != 0)
X {
X
X if (sparse_ind > sp_array_size - 1)
X {
X
X /*
X * realloc the scratch area, since we've run out of room --
X */
X sparsearray = (struct sp_array *)
X ck_realloc (sparsearray,
X 2 * sp_array_size * (sizeof (struct sp_array)));
X sp_array_size *= 2;
X }
X if (cc == sizeof buf)
X {
X if (zero_record (buf))
X {
X if (amidst_data)
X {
X sparsearray[sparse_ind++].numbytes
X = numbytes;
X amidst_data = 0;
X }
X }
X else
X { /* !zero_record(buf) */
X if (amidst_data)
X numbytes += cc;
X else
X {
X amidst_data = 1;
X numbytes = cc;
X sparsearray[sparse_ind].offset
X = offset;
X }
X }
X }
X else if (cc < sizeof buf)
X {
X /* This has to be the last bit of the file, so this */
X /* is somewhat shorter than the above. */
X if (!zero_record (buf))
X {
X if (!amidst_data)
X {
X amidst_data = 1;
X numbytes = cc;
X sparsearray[sparse_ind].offset
X = offset;
X }
X else
X numbytes += cc;
X }
X }
X offset += cc;
X clear_buffer (buf);
X }
X if (amidst_data)
X sparsearray[sparse_ind++].numbytes = numbytes;
X else
X {
X sparsearray[sparse_ind].offset = offset-1;
X sparsearray[sparse_ind++].numbytes = 1;
X }
X close (fd);
X
X return sparse_ind - 1;
X}
X
X/*
X * Just zeroes out the buffer so we don't confuse ourselves with leftover
X * data.
X */
Xvoid
Xclear_buffer (buf)
X char *buf;
X{
X register int i;
X
X for (i = 0; i < RECORDSIZE; i++)
X buf[i] = '\0';
X}
X
X#if 0 /* I'm leaving this as a monument to Joy Kendall, who wrote it -mib */
X/*
X * JK -
X * This routine takes a character array, and tells where within that array
X * the data can be found. It skips over any zeros, and sets the first
X * non-zero point in the array to be the "start", and continues until it
X * finds non-data again, which is marked as the "end." This routine is
X * mainly for 1) seeing how far into a file we must lseek to data, given
X * that we have a sparse file, and 2) determining the "real size" of the
X * file, i.e., the number of bytes in the sparse file that are data, as
X * opposed to the zeros we are trying to skip.
X */
Xwhere_is_data (from, to, buffer)
X int *from, *to;
X char *buffer;
X{
X register int i = 0;
X register int save_to = *to;
X int amidst_data = 0;
X
X
X while (!buffer[i])
X i++;
X *from = i;
X
X if (*from < 16) /* don't bother */
X *from = 0;
X /* keep going to make sure there isn't more real
X data in this record */
X while (i < RECORDSIZE)
X {
X if (!buffer[i])
X {
X if (amidst_data)
X {
X save_to = i;
X amidst_data = 0;
X }
X i++;
X }
X else if (buffer[i])
X {
X if (!amidst_data)
X amidst_data = 1;
X i++;
X }
X }
X if (i == RECORDSIZE)
X *to = i;
X else
X *to = save_to;
X
X}
X
X#endif
X
X/* Note that this routine is only called if zero_record returned true */
X#if 0 /* But we actually don't need it at all. */
Xwhere_is_data (from, to, buffer)
X int *from, *to;
X char *buffer;
X{
X char *fp, *tp;
X
X for (fp = buffer; !*fp; fp++)
X ;
X for (tp = buffer + RECORDSIZE - 1; !*tp; tp--)
X ;
X *from = fp - buffer;
X *to = tp - buffer + 1;
X}
X
X#endif
X
X
X
X/*
X * Takes a recordful of data and basically cruises through it to see if
X * it's made *entirely* of zeros, returning a 0 the instant it finds
X * something that is a non-zero, i.e., useful data.
X */
Xint
Xzero_record (buffer)
X char *buffer;
X{
X register int i;
X
X for (i = 0; i < RECORDSIZE; i++)
X if (buffer[i] != '\000')
X return 0;
X return 1;
X}
X
Xvoid
Xfind_new_file_size (filesize, highest_index)
X int *filesize;
X int highest_index;
X{
X register int i;
X
X *filesize = 0;
X for (i = 0; sparsearray[i].numbytes && i <= highest_index; i++)
X *filesize += sparsearray[i].numbytes;
X}
X
X/*
X * Make a header block for the file name whose stat info is st .
X * Return header pointer for success, NULL if the name is too long.
X */
Xunion record *
Xstart_header (name, st)
X char *name;
X register struct stat *st;
X{
X register union record *header;
X
X if (strlen (name) >= NAMSIZ)
X write_long (name, LF_LONGNAME);
X
X header = (union record *) findrec ();
X bzero (header->charptr, sizeof (*header)); /* XXX speed up */
X
X /*
X * Check the file name and put it in the record.
X */
X if (!f_absolute_paths)
X {
X static int warned_once = 0;
X#ifdef __MSDOS__
X if (name[1] == ':')
X {
X name += 2;
X if (!warned_once++)
X msg ("Removing drive spec from names in the archive");
X }
X#endif
X while ('/' == *name)
X {
X name++; /* Force relative path */
X if (!warned_once++)
X msg ("Removing leading / from absolute path names in the archive.");
X }
X }
X current_file_name = name;
X strncpy (header->header.arch_name, name, NAMSIZ);
X header->header.arch_name[NAMSIZ - 1] = '\0';
X
X to_oct ((long) (f_oldarch ? (st->st_mode & 07777) : st->st_mode),
X 8, header->header.mode);
X to_oct ((long) st->st_uid, 8, header->header.uid);
X to_oct ((long) st->st_gid, 8, header->header.gid);
X to_oct ((long) st->st_size, 1 + 12, header->header.size);
X to_oct ((long) st->st_mtime, 1 + 12, header->header.mtime);
X /* header->header.linkflag is left as null */
X if (f_gnudump)
X {
X to_oct ((long) st->st_atime, 1 + 12, header->header.atime);
X to_oct ((long) st->st_ctime, 1 + 12, header->header.ctime);
X }
X
X#ifndef NONAMES
X /* Fill in new Unix Standard fields if desired. */
X if (f_standard)
X {
X header->header.linkflag = LF_NORMAL; /* New default */
X strcpy (header->header.magic, TMAGIC); /* Mark as Unix Std */
X finduname (header->header.uname, st->st_uid);
X findgname (header->header.gname, st->st_gid);
X }
X#endif
X return header;
X}
X
X/*
X * Finish off a filled-in header block and write it out.
X * We also print the file name and/or full info if verbose is on.
X */
Xvoid
Xfinish_header (header)
X register union record *header;
X{
X register int i, sum;
X register char *p;
X
X bcopy (CHKBLANKS, header->header.chksum, sizeof (header->header.chksum));
X
X sum = 0;
X p = header->charptr;
X for (i = sizeof (*header); --i >= 0;)
X {
X /*
X * We can't use unsigned char here because of old compilers,
X * e.g. V7.
X */
X sum += 0xFF & *p++;
X }
X
X /*
X * Fill in the checksum field. It's formatted differently
X * from the other fields: it has [6] digits, a null, then a
X * space -- rather than digits, a space, then a null.
X * We use to_oct then write the null in over to_oct's space.
X * The final space is already there, from checksumming, and
X * to_oct doesn't modify it.
X *
X * This is a fast way to do:
X * (void) sprintf(header->header.chksum, "%6o", sum);
X */
X to_oct ((long) sum, 8, header->header.chksum);
X header->header.chksum[6] = '\0'; /* Zap the space */
X
X userec (header);
X
X if (f_verbose)
X {
X extern union record *head;/* Points to current tape header */
X extern int head_standard; /* Tape header is in ANSI format */
X
X /* These globals are parameters to print_header, sigh */
X head = header;
X /* hstat is already set up */
X head_standard = f_standard;
X print_header ();
X }
X
X return;
X}
X
X
X/*
X * Quick and dirty octal conversion.
X * Converts long "value" into a "digs"-digit field at "where",
X * including a trailing space and room for a null. "digs"==3 means
X * 1 digit, a space, and room for a null.
X *
X * We assume the trailing null is already there and don't fill it in.
X * This fact is used by start_header and finish_header, so don't change it!
X *
X * This should be equivalent to:
X * (void) sprintf(where, "%*lo ", digs-2, value);
X * except that sprintf fills in the trailing null and we don't.
X */
Xvoid
Xto_oct (value, digs, where)
X register long value;
X register int digs;
X register char *where;
X{
X
X --digs; /* Trailing null slot is left alone */
X where[--digs] = ' '; /* Put in the space, though */
X
X /* Produce the digits -- at least one */
X do
X {
X where[--digs] = '0' + (char) (value & 7); /* one octal digit */
X value >>= 3;
X }
X while (digs > 0 && value != 0);
X
X /* Leading spaces, if necessary */
X while (digs > 0)
X where[--digs] = ' ';
X
X}
X
X
X/*
X * Write the EOT record(s).
X * We actually zero at least one record, through the end of the block.
X * Old tar writes garbage after two zeroed records -- and PDtar used to.
X */
Xvoid
Xwrite_eot ()
X{
X union record *p;
X int bufsize;
X
X p = findrec ();
X if (p)
X {
X bufsize = endofrecs ()->charptr - p->charptr;
X bzero (p->charptr, bufsize);
X userec (p);
X }
X}
X
X/* Write a LF_LONGLINK or LF_LONGNAME record. */
Xvoid
Xwrite_long (p, type)
X char *p;
X char type;
X{
X int size = strlen (p) + 1;
X int bufsize;
X union record *header;
X struct stat foo;
X
X
X bzero (&foo, sizeof foo);
X foo.st_size = size;
X
X header = start_header ("././@LongLink", &foo);
X header->header.linkflag = type;
X finish_header (header);
X
X header = findrec ();
X
X bufsize = endofrecs ()->charptr - header->charptr;
X
X while (bufsize < size)
X {
X bcopy (p, header->charptr, bufsize);
X p += bufsize;
X size -= bufsize;
X userec (header + (bufsize - 1) / RECORDSIZE);
X header = findrec ();
X bufsize = endofrecs ()->charptr - header->charptr;
X }
X bcopy (p, header->charptr, size);
X bzero (header->charptr + size, bufsize - size);
X userec (header + (size - 1) / RECORDSIZE);
X}
END_OF_FILE
if test 33888 -ne `wc -c <'create.c'`; then
echo shar: \"'create.c'\" unpacked with wrong size!
fi
# end of 'create.c'
fi
if test -f 'extract.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'extract.c'\"
else
echo shar: Extracting \"'extract.c'\" \(24051 characters\)
sed "s/^X//" >'extract.c' <<'END_OF_FILE'
X/* Extract files from a tar archive.
X Copyright (C) 1988, 1992, 1993 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/*
X * Extract files from a tar archive.
X *
X * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
X */
X
X#include <stdio.h>
X#include <errno.h>
X#ifndef STDC_HEADERS
Xextern int errno;
X#endif
X#include <sys/types.h>
X#include <time.h>
Xtime_t time ();
X
X#ifdef BSD42
X#include <sys/file.h>
X#else
X#ifndef V7
X#include <fcntl.h>
X#endif
X#endif
X
X#ifdef NO_OPEN3
X/* We need the #define's even though we don't use them. */
X#include "open3.h"
X#endif
X
X#ifdef EMUL_OPEN3
X/* Simulated 3-argument open for systems that don't have it */
X#include "open3.h"
X#endif
X
X#include "tar.h"
X#include "port.h"
X
X#if defined(_POSIX_VERSION)
X#include <utime.h>
X#else
Xstruct utimbuf
X{
X long actime;
X long modtime;
X};
X
X#endif
X
Xextern FILE *msg_file;
X
Xextern union record *head; /* Points to current tape header */
Xextern struct stat hstat; /* Stat struct corresponding */
Xextern int head_standard; /* Tape header is in ANSI format */
X
Xextern char *save_name;
Xextern long save_totsize;
Xextern long save_sizeleft;
X
Xint confirm ();
Xvoid decode_header ();
Xvoid extract_mangle ();
Xvoid extract_sparse_file ();
Xlong from_oct ();
Xvoid gnu_restore ();
Xextern void print_header ();
Xextern void skip_file ();
Xextern void skip_extended_headers ();
Xextern void pr_mkdir ();
Xvoid saverec ();
X
Xint make_dirs (); /* Makes required directories */
X
Xstatic time_t now = 0; /* Current time */
Xstatic we_are_root = 0; /* True if our effective uid == 0 */
Xstatic int notumask = ~0; /* Masks out bits user doesn't want */
X
X/*
X * "Scratch" space to store the information about a sparse file before
X * writing the info into the header or extended header
X */
X/*struct sp_array *sparsearray;*/
X
X/* number of elts storable in the sparsearray */
X/*int sp_array_size = 10;*/
X
Xstruct saved_dir_info
X{
X char *path;
X int mode;
X int atime;
X int mtime;
X struct saved_dir_info *next;
X};
X
Xstruct saved_dir_info *saved_dir_info_head;
X
X/*
X * Set up to extract files.
X */
Xvoid
Xextr_init ()
X{
X int ourmask;
X
X now = time ((time_t *) 0);
X if (geteuid () == 0)
X we_are_root = 1;
X
X /*
X * We need to know our umask. But if f_use_protection is set,
X * leave our kernel umask at 0, and our "notumask" at ~0.
X */
X ourmask = umask (0); /* Read it */
X if (!f_use_protection)
X {
X (void) umask (ourmask); /* Set it back how it was */
X notumask = ~ourmask; /* Make umask override permissions */
X }
X}
X
X
X/*
X * Extract a file from the archive.
X */
Xvoid
Xextract_archive ()
X{
X register char *data;
X int fd, check, namelen, written, openflag;
X long size;
X struct utimbuf acc_upd_times;
X register int skipcrud;
X register int i;
X /* int sparse_ind = 0;*/
X union record *exhdr;
X struct saved_dir_info *tmp;
X /* int end_nulls; */
X
X saverec (&head); /* Make sure it sticks around */
X userec (head); /* And go past it in the archive */
X decode_header (head, &hstat, &head_standard, 1); /* Snarf fields */
X
X if (f_confirm && !confirm ("extract", current_file_name))
X {
X if (head->header.isextended)
X skip_extended_headers ();
X skip_file ((long) hstat.st_size);
X saverec ((union record **) 0);
X return;
X }
X
X /* Print the record from 'head' and 'hstat' */
X if (f_verbose)
X print_header ();
X
X /*
X * Check for fully specified pathnames and other atrocities.
X *
X * Note, we can't just make a pointer to the new file name,
X * since saverec() might move the header and adjust "head".
X * We have to start from "head" every time we want to touch
X * the header record.
X */
X skipcrud = 0;
X while (!f_absolute_paths
X && '/' == current_file_name[skipcrud])
X {
X static int warned_once = 0;
X
X skipcrud++; /* Force relative path */
X if (!warned_once++)
X {
X msg ("Removing leading / from absolute path names in the archive.");
X }
X }
X
X switch (head->header.linkflag)
X {
X
X default:
X msg ("Unknown file type '%c' for %s, extracted as normal file",
X head->header.linkflag, skipcrud + current_file_name);
X /* FALL THRU */
X
X /*
X * JK - What we want to do if the file is sparse is loop through
X * the array of sparse structures in the header and read in
X * and translate the character strings representing 1) the offset
X * at which to write and 2) how many bytes to write into numbers,
X * which we store into the scratch array, "sparsearray". This
X * array makes our life easier the same way it did in creating
X * the tar file that had to deal with a sparse file.
X *
X * After we read in the first five (at most) sparse structures,
X * we check to see if the file has an extended header, i.e.,
X * if more sparse structures are needed to describe the contents
X * of the new file. If so, we read in the extended headers
X * and continue to store their contents into the sparsearray.
X */
X case LF_SPARSE:
X sp_array_size = 10;
X sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
X for (i = 0; i < SPARSE_IN_HDR; i++)
X {
X sparsearray[i].offset =
X from_oct (1 + 12, head->header.sp[i].offset);
X sparsearray[i].numbytes =
X from_oct (1 + 12, head->header.sp[i].numbytes);
X if (!sparsearray[i].numbytes)
X break;
X }
X
X /* end_nulls = from_oct(1+12, head->header.ending_blanks);*/
X
X if (head->header.isextended)
X {
X /* read in the list of extended headers
X and translate them into the sparsearray
X as before */
X
X /* static */ int ind = SPARSE_IN_HDR;
X
X for (;;)
X {
X
X exhdr = findrec ();
X for (i = 0; i < SPARSE_EXT_HDR; i++)
X {
X
X if (i + ind > sp_array_size - 1)
X {
X /*
X * realloc the scratch area
X * since we've run out of room --
X */
X sparsearray = (struct sp_array *)
X ck_realloc (sparsearray,
X 2 * sp_array_size * (sizeof (struct sp_array)));
X sp_array_size *= 2;
X }
X if (!exhdr->ext_hdr.sp[i].numbytes)
X break;
X sparsearray[i + ind].offset =
X from_oct (1 + 12, exhdr->ext_hdr.sp[i].offset);
X sparsearray[i + ind].numbytes =
X from_oct (1 + 12, exhdr->ext_hdr.sp[i].numbytes);
X }
X if (!exhdr->ext_hdr.isextended)
X break;
X else
X {
X ind += SPARSE_EXT_HDR;
X userec (exhdr);
X }
X }
X userec (exhdr);
X }
X
X /* FALL THRU */
X case LF_OLDNORMAL:
X case LF_NORMAL:
X case LF_CONTIG:
X /*
X * Appears to be a file.
X * See if it's really a directory.
X */
X namelen = strlen (skipcrud + current_file_name) - 1;
X if (current_file_name[skipcrud + namelen] == '/')
X goto really_dir;
X
X /* FIXME, deal with protection issues */
X again_file:
X openflag = (f_keep ?
X O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_EXCL :
X O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_TRUNC)
X | ((head->header.linkflag == LF_SPARSE) ? 0 : O_APPEND);
X /*
X * JK - The last | is a kludge to solve the problem
X * the O_APPEND flag causes with files we are
X * trying to make sparse: when a file is opened
X * with O_APPEND, it writes to the last place
X * that something was written, thereby ignoring
X * any lseeks that we have done. We add this
X * extra condition to make it able to lseek when
X * a file is sparse, i.e., we don't open the new
X * file with this flag. (Grump -- this bug caused
X * me to waste a good deal of time, I might add)
X */
X
X if (f_exstdout)
X {
X fd = 1;
X goto extract_file;
X }
X#ifdef O_CTG
X /*
X * Contiguous files (on the Masscomp) have to specify
X * the size in the open call that creates them.
X */
X if (head->header.linkflag == LF_CONTIG)
X fd = open ((longname ? longname : head->header.name)
X + skipcrud,
X openflag | O_CTG,
X hstat.st_mode, hstat.st_size);
X else
X#endif
X {
X#ifdef NO_OPEN3
X /*
X * On raw V7 we won't let them specify -k (f_keep), but
X * we just bull ahead and create the files.
X */
X fd = creat ((longname
X ? longname
X : head->header.name) + skipcrud,
X hstat.st_mode);
X#else
X /*
X * With 3-arg open(), we can do this up right.
X */
X fd = open (skipcrud + current_file_name,
X openflag, hstat.st_mode);
X#endif
X }
X
X if (fd < 0)
X {
X if (make_dirs (skipcrud + current_file_name))
X goto again_file;
X msg_perror ("Could not create file %s",
X skipcrud + current_file_name);
X if (head->header.isextended)
X skip_extended_headers ();
X skip_file ((long) hstat.st_size);
X goto quit;
X }
X
X extract_file:
X if (head->header.linkflag == LF_SPARSE)
X {
X char *name;
X int namelen;
X
X /*
X * Kludge alert. NAME is assigned to header.name
X * because during the extraction, the space that
X * contains the header will get scribbled on, and
X * the name will get munged, so any error messages
X * that happen to contain the filename will look
X * REAL interesting unless we do this.
X */
X namelen = strlen (skipcrud + current_file_name) + 1;
X name = (char *) ck_malloc ((sizeof (char)) * namelen);
X bcopy (skipcrud + current_file_name, name, namelen);
X size = hstat.st_size;
X extract_sparse_file (fd, &size, hstat.st_size, name);
X }
X else
X for (size = hstat.st_size;
X size > 0;
X size -= written)
X {
X
X /* long offset,
X numbytes;*/
X
X if (f_multivol)
X {
X save_name = current_file_name;
X save_totsize = hstat.st_size;
X save_sizeleft = size;
X }
X
X /*
X * Locate data, determine max length
X * writeable, write it, record that
X * we have used the data, then check
X * if the write worked.
X */
X data = findrec ()->charptr;
X if (data == NULL)
X { /* Check it... */
X msg ("Unexpected EOF on archive file");
X break;
X }
X /*
X * JK - If the file is sparse, use the sparsearray
X * that we created before to lseek into the new
X * file the proper amount, and to see how many
X * bytes we want to write at that position.
X */
X /* if (head->header.linkflag == LF_SPARSE) {
X off_t pos;
X
X pos = lseek(fd, (off_t) sparsearray[sparse_ind].offset, 0);
X printf("%d at %d\n", (int) pos, sparse_ind);
X written = sparsearray[sparse_ind++].numbytes;
X } else*/
X written = endofrecs ()->charptr - data;
X if (written > size)
X written = size;
X errno = 0;
X check = write (fd, data, written);
X /*
X * The following is in violation of strict
X * typing, since the arg to userec
X * should be a struct rec *. FIXME.
X */
X userec ((union record *) (data + written - 1));
X if (check == written)
X continue;
X /*
X * Error in writing to file.
X * Print it, skip to next file in archive.
X */
X if (check < 0)
X msg_perror ("couldn't write to file %s",
X skipcrud + current_file_name);
X else
X msg ("could only write %d of %d bytes to file %s",
X check, written, skipcrud + current_file_name);
X skip_file ((long) (size - written));
X break; /* Still do the close, mod time, chmod, etc */
X }
X
X if (f_multivol)
X save_name = 0;
X
X /* If writing to stdout, don't try to do anything
X to the filename; it doesn't exist, or we don't
X want to touch it anyway */
X if (f_exstdout)
X break;
X
X /* if (head->header.isextended) {
X register union record *exhdr;
X register int i;
X
X for (i = 0; i < 21; i++) {
X long offset;
X
X if (!exhdr->ext_hdr.sp[i].numbytes)
X break;
X offset = from_oct(1+12,
X exhdr->ext_hdr.sp[i].offset);
X written = from_oct(1+12,
X exhdr->ext_hdr.sp[i].numbytes);
X lseek(fd, offset, 0);
X check = write(fd, data, written);
X if (check == written) continue;
X
X }
X
X
X }*/
X check = close (fd);
X if (check < 0)
X {
X msg_perror ("Error while closing %s",
X skipcrud + current_file_name);
X }
X
X
X set_filestat:
X
X /*
X * If we are root, set the owner and group of the extracted
X * file. This does what is wanted both on real Unix and on
X * System V. If we are running as a user, we extract as that
X * user; if running as root, we extract as the original owner.
X */
X if (we_are_root || f_do_chown)
X {
X if (chown (skipcrud + current_file_name,
X hstat.st_uid, hstat.st_gid) < 0)
X {
X msg_perror ("cannot chown file %s to uid %d gid %d",
X skipcrud + current_file_name,
X hstat.st_uid, hstat.st_gid);
X }
X }
X
X /*
X * Set the modified time of the file.
X *
X * Note that we set the accessed time to "now", which
X * is really "the time we started extracting files".
X * unless f_gnudump is used, in which case .st_atime is used
X */
X if (!f_modified)
X {
X /* fixme if f_gnudump should set ctime too, but how? */
X if (f_gnudump)
X acc_upd_times.actime = hstat.st_atime;
X else
X acc_upd_times.actime = now; /* Accessed now */
X acc_upd_times.modtime = hstat.st_mtime; /* Mod'd */
X if (utime (skipcrud + current_file_name,
X &acc_upd_times) < 0)
X {
X msg_perror ("couldn't change access and modification times of %s", skipcrud + current_file_name);
X }
X }
X /* We do the utime before the chmod because some versions of
X utime are broken and trash the modes of the file. Since
X we then change the mode anyway, we don't care. . . */
X
X /*
X * If '-k' is not set, open() or creat() could have saved
X * the permission bits from a previously created file,
X * ignoring the ones we specified.
X * Even if -k is set, if the file has abnormal
X * mode bits, we must chmod since writing or chown() has
X * probably reset them.
X *
X * If -k is set, we know *we* created this file, so the mode
X * bits were set by our open(). If the file is "normal", we
X * skip the chmod. This works because we did umask(0) if -p
X * is set, so umask will have left the specified mode alone.
X */
X if ((!f_keep)
X || (hstat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
X {
X if (chmod (skipcrud + current_file_name,
X notumask & (int) hstat.st_mode) < 0)
X {
X msg_perror ("cannot change mode of file %s to %ld",
X skipcrud + current_file_name,
X notumask & (int) hstat.st_mode);
X }
X }
X
X quit:
X break;
X
X case LF_LINK:
X again_link:
X {
X struct stat st1, st2;
X
X check = link (current_link_name, skipcrud + current_file_name);
X
X if (check == 0)
X break;
X if (make_dirs (skipcrud + current_file_name))
X goto again_link;
X if (f_gnudump && errno == EEXIST)
X break;
X if (stat (current_link_name, &st1) == 0
X && stat (current_file_name + skipcrud, &st2) == 0
X && st1.st_dev == st2.st_dev
X && st1.st_ino == st2.st_ino)
X break;
X msg_perror ("Could not link %s to %s",
X skipcrud + current_file_name,
X current_link_name);
X }
X break;
X
X#ifdef S_ISLNK
X case LF_SYMLINK:
X again_symlink:
X check = symlink (current_link_name,
X skipcrud + current_file_name);
X /* FIXME, don't worry uid, gid, etc... */
X if (check == 0)
X break;
X if (make_dirs (current_file_name + skipcrud))
X goto again_symlink;
X msg_perror ("Could not create symlink to %s",
X current_link_name);
X break;
X#endif
X
X#ifdef S_IFCHR
X case LF_CHR:
X hstat.st_mode |= S_IFCHR;
X goto make_node;
X#endif
X
X#ifdef S_IFBLK
X case LF_BLK:
X hstat.st_mode |= S_IFBLK;
X#endif
X#if defined(S_IFCHR) || defined(S_IFBLK)
X make_node:
X check = mknod (current_file_name + skipcrud,
X (int) hstat.st_mode, (int) hstat.st_rdev);
X if (check != 0)
X {
X if (make_dirs (skipcrud + current_file_name))
X goto make_node;
X msg_perror ("Could not make %s",
X current_file_name + skipcrud);
X break;
X };
X goto set_filestat;
X#endif
X
X#ifdef S_ISFIFO
X /* If local system doesn't support FIFOs, use default case */
X case LF_FIFO:
X make_fifo:
X check = mkfifo (current_file_name + skipcrud,
X (int) hstat.st_mode);
X if (check != 0)
X {
X if (make_dirs (current_file_name + skipcrud))
X goto make_fifo;
X msg_perror ("Could not make %s",
X skipcrud + current_file_name);
X break;
X };
X goto set_filestat;
X#endif
X
X case LF_DIR:
X case LF_DUMPDIR:
X namelen = strlen (current_file_name + skipcrud) - 1;
X really_dir:
X /* Check for trailing /, and zap as many as we find. */
X while (namelen
X && current_file_name[skipcrud + namelen] == '/')
X current_file_name[skipcrud + namelen--] = '\0';
X if (f_gnudump)
X { /* Read the entry and delete files
X that aren't listed in the archive */
X gnu_restore (skipcrud);
X
X }
X else if (head->header.linkflag == LF_DUMPDIR)
X skip_file ((long) (hstat.st_size));
X
X
X again_dir:
X check = mkdir (skipcrud + current_file_name,
X (we_are_root ? 0 : 0300) | (int) hstat.st_mode);
X if (check != 0)
X {
X struct stat st1;
X
X if (make_dirs (skipcrud + current_file_name))
X goto again_dir;
X /* If we're trying to create '.', let it be. */
X if (current_file_name[skipcrud + namelen] == '.' &&
X (namelen == 0 ||
X current_file_name[skipcrud + namelen - 1] == '/'))
X goto check_perms;
X if (errno == EEXIST
X && stat (skipcrud + current_file_name, &st1) == 0
X && (S_ISDIR (st1.st_mode)))
X break;
X msg_perror ("Could not create directory %s", skipcrud + current_file_name);
X break;
X }
X
X check_perms:
X if (!we_are_root && 0300 != (0300 & (int) hstat.st_mode))
X {
X hstat.st_mode |= 0300;
X msg ("Added write and execute permission to directory %s",
X skipcrud + current_file_name);
X }
X
X /*
X * If we are root, set the owner and group of the extracted
X * file. This does what is wanted both on real Unix and on
X * System V. If we are running as a user, we extract as that
X * user; if running as root, we extract as the original owner.
X */
X if (we_are_root || f_do_chown)
X {
X if (chown (skipcrud + current_file_name,
X hstat.st_uid, hstat.st_gid) < 0)
X {
X msg_perror ("cannot chown file %s to uid %d gid %d",
X skipcrud + current_file_name,
X hstat.st_uid, hstat.st_gid);
X }
X }
X
X if (!f_modified)
X {
X tmp = ((struct saved_dir_info *)
X ck_malloc (sizeof (struct saved_dir_info)));
X tmp->path = (char *) ck_malloc (strlen (skipcrud
X + current_file_name) + 1);
X strcpy (tmp->path, skipcrud + current_file_name);
X tmp->mode = hstat.st_mode;
X tmp->atime = hstat.st_atime;
X tmp->mtime = hstat.st_mtime;
X tmp->next = saved_dir_info_head;
X saved_dir_info_head = tmp;
X }
X else
X {
X /* This functions exactly as the code for set_filestat above. */
X if ((!f_keep)
X || (hstat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
X {
X if (chmod (skipcrud + current_file_name,
X notumask & (int) hstat.st_mode) < 0)
X {
X msg_perror ("cannot change mode of file %s to %ld",
X skipcrud + current_file_name,
X notumask & (int) hstat.st_mode);
X }
X }
X }
X break;
X
X case LF_VOLHDR:
X if (f_verbose)
X {
X printf ("Reading %s\n", current_file_name);
X }
X break;
X
X case LF_NAMES:
X extract_mangle (head);
X break;
X
X case LF_MULTIVOL:
X msg ("Can't extract '%s'--file is continued from another volume\n", current_file_name);
X skip_file ((long) hstat.st_size);
X break;
X
X case LF_LONGNAME:
X case LF_LONGLINK:
X msg ("Visible long name error\n");
X skip_file ((long) hstat.st_size);
X break;
X }
X
X /* We don't need to save it any longer. */
X saverec ((union record **) 0);/* Unsave it */
X}
X
X/*
X * After a file/link/symlink/dir creation has failed, see if
X * it's because some required directory was not present, and if
X * so, create all required dirs.
X */
Xint
Xmake_dirs (pathname)
X char *pathname;
X{
X char *p; /* Points into path */
X int madeone = 0; /* Did we do anything yet? */
X int save_errno = errno; /* Remember caller's errno */
X int check;
X
X if (errno != ENOENT)
X return 0; /* Not our problem */
X
X for (p = index (pathname, '/'); p != NULL; p = index (p + 1, '/'))
X {
X /* Avoid mkdir of empty string, if leading or double '/' */
X if (p == pathname || p[-1] == '/')
X continue;
X /* Avoid mkdir where last part of path is '.' */
X if (p[-1] == '.' && (p == pathname + 1 || p[-2] == '/'))
X continue;
X *p = 0; /* Truncate the path there */
X check = mkdir (pathname, 0777); /* Try to create it as a dir */
X if (check == 0)
X {
X /* Fix ownership */
X if (we_are_root)
X {
X if (chown (pathname, hstat.st_uid,
X hstat.st_gid) < 0)
X {
X msg_perror ("cannot change owner of %s to uid %d gid %d", pathname, hstat.st_uid, hstat.st_gid);
X }
X }
X pr_mkdir (pathname, p - pathname, notumask & 0777);
X madeone++; /* Remember if we made one */
X *p = '/';
X continue;
X }
X *p = '/';
X if (errno == EEXIST) /* Directory already exists */
X continue;
X /*
X * Some other error in the mkdir. We return to the caller.
X */
X break;
X }
X
X errno = save_errno; /* Restore caller's errno */
X return madeone; /* Tell them to retry if we made one */
X}
X
Xvoid
Xextract_sparse_file (fd, sizeleft, totalsize, name)
X int fd;
X long *sizeleft, totalsize;
X char *name;
X{
X /* register char *data;*/
X union record *datarec;
X int sparse_ind = 0;
X int written, count;
X
X /* assuming sizeleft is initially totalsize */
X
X
X while (*sizeleft > 0)
X {
X datarec = findrec ();
X if (datarec == NULL)
X {
X msg ("Unexpected EOF on archive file");
X return;
X }
X lseek (fd, sparsearray[sparse_ind].offset, 0);
X written = sparsearray[sparse_ind++].numbytes;
X while (written > RECORDSIZE)
X {
X count = write (fd, datarec->charptr, RECORDSIZE);
X if (count < 0)
X msg_perror ("couldn't write to file %s", name);
X written -= count;
X *sizeleft -= count;
X userec (datarec);
X datarec = findrec ();
X }
X
X count = write (fd, datarec->charptr, written);
X
X if (count < 0)
X {
X msg_perror ("couldn't write to file %s", name);
X }
X else if (count != written)
X {
X msg ("could only write %d of %d bytes to file %s", count,
X totalsize, name);
X skip_file ((long) (*sizeleft));
X }
X
X written -= count;
X *sizeleft -= count;
X userec (datarec);
X }
X free (sparsearray);
X /* if (end_nulls) {
X register int i;
X
X printf("%d\n", (int) end_nulls);
X for (i = 0; i < end_nulls; i++)
X write(fd, "\000", 1);
X }*/
X userec (datarec);
X}
X
X/* Set back the utime and mode for all the extracted directories. */
Xvoid
Xrestore_saved_dir_info ()
X{
X struct utimbuf acc_upd_times;
X
X while (saved_dir_info_head != NULL)
X {
X /* fixme if f_gnudump should set ctime too, but how? */
X if (f_gnudump)
X acc_upd_times.actime = saved_dir_info_head->atime;
X else
X acc_upd_times.actime = now; /* Accessed now */
X acc_upd_times.modtime = saved_dir_info_head->mtime; /* Mod'd */
X if (utime (saved_dir_info_head->path, &acc_upd_times) < 0)
X {
X msg_perror ("couldn't change access and modification times of %s",
X saved_dir_info_head->path);
X }
X if ((!f_keep) || (saved_dir_info_head->mode & (S_ISUID | S_ISGID | S_ISVTX)))
X {
X if (chmod (saved_dir_info_head->path,
X notumask & saved_dir_info_head->mode) < 0)
X {
X msg_perror ("cannot change mode of file %s to %ld",
X saved_dir_info_head->path,
X notumask & saved_dir_info_head->mode);
X }
X }
X saved_dir_info_head = saved_dir_info_head->next;
X }
X}
END_OF_FILE
if test 24051 -ne `wc -c <'extract.c'`; then
echo shar: \"'extract.c'\" unpacked with wrong size!
fi
# end of 'extract.c'
fi
if test -f 'buffer.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'buffer.c'\"
else
echo shar: Extracting \"'buffer.c'\" \(35194 characters\)
sed "s/^X//" >'buffer.c' <<'END_OF_FILE'
X/* Buffer management for tar.
X Copyright (C) 1988, 1992, 1993 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/*
X * Buffer management for tar.
X *
X * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
X */
X
X#include <stdio.h>
X#include <errno.h>
X#ifndef STDC_HEADERS
Xextern int errno;
X#endif
X#include <sys/types.h> /* For non-Berkeley systems */
X#include <signal.h>
X#include <time.h>
Xtime_t time ();
X
X#ifdef HAVE_SYS_MTIO_H
X#include <sys/ioctl.h>
X#include <sys/mtio.h>
X#endif
X
X#ifdef BSD42
X#include <sys/file.h>
X#else
X#ifndef V7
X#include <fcntl.h>
X#endif
X#endif
X
X#ifdef __MSDOS__
X#include <process.h>
X#endif
X
X#ifdef XENIX
X#include <sys/inode.h>
X#endif
X
X#include "tar.h"
X#include "port.h"
X#include "rmt.h"
X#include "regex.h"
X
X/* Either stdout or stderr: The thing we write messages (standard msgs, not
X errors) to. Stdout unless we're writing a pipe, in which case stderr */
XFILE *msg_file = stdout;
X
X#define STDIN 0 /* Standard input file descriptor */
X#define STDOUT 1 /* Standard output file descriptor */
X
X#define PREAD 0 /* Read file descriptor from pipe() */
X#define PWRITE 1 /* Write file descriptor from pipe() */
X
X#define MAGIC_STAT 105 /* Magic status returned by child, if
X it can't exec. We hope compress/sh
X never return this status! */
X
Xvoid *valloc ();
X
Xvoid writeerror ();
Xvoid readerror ();
X
Xvoid ck_pipe ();
Xvoid ck_close ();
X
Xint backspace_output ();
Xextern void finish_header ();
Xvoid flush_archive ();
Xint isfile ();
Xint new_volume ();
Xvoid verify_volume ();
Xextern void to_oct ();
X
X#ifndef __MSDOS__
X/* Obnoxious test to see if dimwit is trying to dump the archive */
Xdev_t ar_dev;
Xino_t ar_ino;
X#endif
X
X/*
X * The record pointed to by save_rec should not be overlaid
X * when reading in a new tape block. Copy it to record_save_area first, and
X * change the pointer in *save_rec to point to record_save_area.
X * Saved_recno records the record number at the time of the save.
X * This is used by annofile() to print the record number of a file's
X * header record.
X */
Xstatic union record **save_rec;
Xunion record record_save_area;
Xstatic long saved_recno;
X
X/*
X * PID of child program, if f_compress or remote archive access.
X */
Xstatic int childpid = 0;
X
X/*
X * Record number of the start of this block of records
X */
Xlong baserec;
X
X/*
X * Error recovery stuff
X */
Xstatic int r_error_count;
X
X/*
X * Have we hit EOF yet?
X */
Xstatic int hit_eof;
X
X/* Checkpointing counter */
Xstatic int checkpoint;
X
X/* JF we're reading, but we just read the last record and its time to update */
Xextern time_to_start_writing;
Xint file_to_switch_to = -1; /* If remote update, close archive, and use
X this descriptor to write to */
X
Xstatic int volno = 1; /* JF which volume of a multi-volume tape
X we're on */
Xstatic int global_volno = 1; /* Volume number to print in external messages. */
X
Xchar *save_name = 0; /* Name of the file we are currently writing */
Xlong save_totsize; /* total size of file we are writing. Only
X valid if save_name is non_zero */
Xlong save_sizeleft; /* Where we are in the file we are writing.
X Only valid if save_name is non-zero */
X
Xint write_archive_to_stdout;
X
X/* Used by fl_read and fl_write to store the real info about saved names */
Xstatic char real_s_name[NAMSIZ];
Xstatic long real_s_totsize;
Xstatic long real_s_sizeleft;
X
X/* Reset the EOF flag (if set), and re-set ar_record, etc */
X
Xvoid
Xreset_eof ()
X{
X if (hit_eof)
X {
X hit_eof = 0;
X ar_record = ar_block;
X ar_last = ar_block + blocking;
X ar_reading = 0;
X }
X}
X
X/*
X * Return the location of the next available input or output record.
X * Return NULL for EOF. Once we have returned NULL, we just keep returning
X * it, to avoid accidentally going on to the next file on the "tape".
X */
Xunion record *
Xfindrec ()
X{
X if (ar_record == ar_last)
X {
X if (hit_eof)
X return (union record *) NULL; /* EOF */
X flush_archive ();
X if (ar_record == ar_last)
X {
X hit_eof++;
X return (union record *) NULL; /* EOF */
X }
X }
X return ar_record;
X}
X
X
X/*
X * Indicate that we have used all records up thru the argument.
X * (should the arg have an off-by-1? XXX FIXME)
X */
Xvoid
Xuserec (rec)
X union record *rec;
X{
X while (rec >= ar_record)
X ar_record++;
X /*
X * Do NOT flush the archive here. If we do, the same
X * argument to userec() could mean the next record (if the
X * input block is exactly one record long), which is not what
X * is intended.
X */
X if (ar_record > ar_last)
X abort ();
X}
X
X
X/*
X * Return a pointer to the end of the current records buffer.
X * All the space between findrec() and endofrecs() is available
X * for filling with data, or taking data from.
X */
Xunion record *
Xendofrecs ()
X{
X return ar_last;
X}
X
X
X/*
X * Duplicate a file descriptor into a certain slot.
X * Equivalent to BSD "dup2" with error reporting.
X */
Xvoid
Xdupto (from, to, msg)
X int from, to;
X char *msg;
X{
X int err;
X
X if (from != to)
X {
X err = close (to);
X if (err < 0 && errno != EBADF)
X {
X msg_perror ("Cannot close descriptor %d", to);
X exit (EX_SYSTEM);
X }
X err = dup (from);
X if (err != to)
X {
X msg_perror ("cannot dup %s", msg);
X exit (EX_SYSTEM);
X }
X ck_close (from);
X }
X}
X
X#ifdef __MSDOS__
Xvoid
Xchild_open ()
X{
X fprintf (stderr, "MS-DOS %s can't use compressed or remote archives\n", tar);
X exit (EX_ARGSBAD);
X}
X
X#else
Xvoid
Xchild_open ()
X{
X int pipe[2];
X int err = 0;
X
X int kidpipe[2];
X int kidchildpid;
X
X#define READ 0
X#define WRITE 1
X
X ck_pipe (pipe);
X
X childpid = fork ();
X if (childpid < 0)
X {
X msg_perror ("cannot fork");
X exit (EX_SYSTEM);
X }
X if (childpid > 0)
X {
X /* We're the parent. Clean up and be happy */
X /* This, at least, is easy */
X
X if (ar_reading)
X {
X f_reblock++;
X archive = pipe[READ];
X ck_close (pipe[WRITE]);
X }
X else
X {
X archive = pipe[WRITE];
X ck_close (pipe[READ]);
X }
X return;
X }
X
X /* We're the kid */
X if (ar_reading)
X {
X dupto (pipe[WRITE], STDOUT, "(child) pipe to stdout");
X ck_close (pipe[READ]);
X }
X else
X {
X dupto (pipe[READ], STDIN, "(child) pipe to stdin");
X ck_close (pipe[WRITE]);
X }
X
X /* We need a child tar only if
X 1: we're reading/writing stdin/out (to force reblocking)
X 2: the file is to be accessed by rmt (compress doesn't know how)
X 3: the file is not a plain file */
X#ifdef NO_REMOTE
X if (!(ar_files[0][0] == '-' && ar_files[0][1] == '\0') && isfile (ar_files[0]))
X#else
X if (!(ar_files[0][0] == '-' && ar_files[0][1] == '\0') && !_remdev (ar_files[0]) && isfile (ar_files[0]))
X#endif
X {
X /* We don't need a child tar. Open the archive */
X if (ar_reading)
X {
X archive = open (ar_files[0], O_RDONLY | O_BINARY, 0666);
X if (archive < 0)
X {
X msg_perror ("can't open archive %s", ar_files[0]);
X exit (EX_BADARCH);
X }
X dupto (archive, STDIN, "archive to stdin");
X /* close(archive); */
X }
X else
X {
X archive = creat (ar_files[0], 0666);
X if (archive < 0)
X {
X msg_perror ("can't open archive %s", ar_files[0]);
X exit (EX_BADARCH);
X }
X dupto (archive, STDOUT, "archive to stdout");
X /* close(archive); */
X }
X }
X else
X {
X /* We need a child tar */
X ck_pipe (kidpipe);
X
X kidchildpid = fork ();
X if (kidchildpid < 0)
X {
X msg_perror ("child can't fork");
X exit (EX_SYSTEM);
X }
X
X if (kidchildpid > 0)
X {
X /* About to exec compress: set up the files */
X if (ar_reading)
X {
X dupto (kidpipe[READ], STDIN, "((child)) pipe to stdin");
X ck_close (kidpipe[WRITE]);
X /* dup2(pipe[WRITE],STDOUT); */
X }
X else
X {
X /* dup2(pipe[READ],STDIN); */
X dupto (kidpipe[WRITE], STDOUT, "((child)) pipe to stdout");
X ck_close (kidpipe[READ]);
X }
X /* ck_close(pipe[READ]); */
X /* ck_close(pipe[WRITE]); */
X /* ck_close(kidpipe[READ]);
X ck_close(kidpipe[WRITE]); */
X }
X else
X {
X /* Grandchild. Do the right thing, namely sit here and
X read/write the archive, and feed stuff back to compress */
X tar = "tar (child)";
X if (ar_reading)
X {
X dupto (kidpipe[WRITE], STDOUT, "[child] pipe to stdout");
X ck_close (kidpipe[READ]);
X }
X else
X {
X dupto (kidpipe[READ], STDIN, "[child] pipe to stdin");
X ck_close (kidpipe[WRITE]);
X }
X
X if (ar_files[0][0] == '-' && ar_files[0][1] == '\0')
X {
X if (ar_reading)
X archive = STDIN;
X else
X archive = STDOUT;
X }
X else /* This can't happen if (ar_reading==2)
X archive = rmtopen(ar_files[0], O_RDWR|O_CREAT|O_BINARY, 0666);
X else */ if (ar_reading)
X archive = rmtopen (ar_files[0], O_RDONLY | O_BINARY, 0666);
X else
X archive = rmtcreat (ar_files[0], 0666);
X
X if (archive < 0)
X {
X msg_perror ("can't open archive %s", ar_files[0]);
X exit (EX_BADARCH);
X }
X
X if (ar_reading)
X {
X for (;;)
X {
X char *ptr;
X int max, count;
X
X r_error_count = 0;
X error_loop:
X err = rmtread (archive, ar_block->charptr, (int) (blocksize));
X if (err < 0)
X {
X readerror ();
X goto error_loop;
X }
X if (err == 0)
X break;
X ptr = ar_block->charptr;
X max = err;
X while (max)
X {
X count = (max < RECORDSIZE) ? max : RECORDSIZE;
X err = write (STDOUT, ptr, count);
X if (err != count)
X {
X if (err < 0)
X {
X msg_perror ("can't write to compression program");
X exit (EX_SYSTEM);
X }
X else
X msg ("write to compression program short %d bytes",
X count - err);
X count = (err < 0) ? 0 : err;
X }
X ptr += count;
X max -= count;
X }
X }
X }
X else
X {
X for (;;)
X {
X int n;
X char *ptr;
X
X n = blocksize;
X ptr = ar_block->charptr;
X while (n)
X {
X err = read (STDIN, ptr, (n < RECORDSIZE) ? n : RECORDSIZE);
X if (err <= 0)
X break;
X n -= err;
X ptr += err;
X }
X /* EOF */
X if (err == 0)
X {
X if (!f_compress_block)
X blocksize -= n;
X else
X bzero (ar_block->charptr + blocksize - n, n);
X err = rmtwrite (archive, ar_block->charptr, blocksize);
X if (err != (blocksize))
X writeerror (err);
X if (!f_compress_block)
X blocksize += n;
X break;
X }
X if (n)
X {
X msg_perror ("can't read from compression program");
X exit (EX_SYSTEM);
X }
X err = rmtwrite (archive, ar_block->charptr, (int) blocksize);
X if (err != blocksize)
X writeerror (err);
X }
X }
X
X /* close_archive(); */
X exit (0);
X }
X }
X /* So we should exec compress (-d) */
X if (ar_reading)
X execlp (f_compressprog, f_compressprog, "-d", (char *) 0);
X else
X execlp (f_compressprog, f_compressprog, (char *) 0);
X msg_perror ("can't exec %s", f_compressprog);
X _exit (EX_SYSTEM);
X}
X
X
X/* return non-zero if p is the name of a directory */
Xint
Xisfile (p)
X char *p;
X{
X struct stat stbuf;
X
X if (stat (p, &stbuf) < 0)
X return 1;
X if (S_ISREG (stbuf.st_mode))
X return 1;
X return 0;
X}
X
X#endif
X
X/*
X * Open an archive file. The argument specifies whether we are
X * reading or writing.
X */
X/* JF if the arg is 2, open for reading and writing. */
Xvoid
Xopen_archive (reading)
X int reading;
X{
X msg_file = f_exstdout ? stderr : stdout;
X
X if (blocksize == 0)
X {
X msg ("invalid value for blocksize");
X exit (EX_ARGSBAD);
X }
X
X if (n_ar_files == 0)
X {
X msg ("No archive name given, what should I do?");
X exit (EX_BADARCH);
X }
X
X /*NOSTRICT*/
X if (f_multivol)
X {
X ar_block = (union record *) valloc ((unsigned) (blocksize + (2 * RECORDSIZE)));
X if (ar_block)
X ar_block += 2;
X }
X else
X ar_block = (union record *) valloc ((unsigned) blocksize);
X if (!ar_block)
X {
X msg ("could not allocate memory for blocking factor %d",
X blocking);
X exit (EX_ARGSBAD);
X }
X
X ar_record = ar_block;
X ar_last = ar_block + blocking;
X ar_reading = reading;
X
X if (f_multivol && f_verify)
X {
X msg ("cannot verify multi-volume archives");
X exit (EX_ARGSBAD);
X }
X
X if (f_compressprog)
X {
X if (reading == 2 || f_verify)
X {
X msg ("cannot update or verify compressed archives");
X exit (EX_ARGSBAD);
X }
X if (f_multivol)
X {
X msg ("cannot use multi-volume compressed archives");
X exit (EX_ARGSBAD);
X }
X child_open ();
X if (!reading && ar_files[0][0] == '-' && ar_files[0][1] == '\0')
X msg_file = stderr;
X /* child_open(rem_host, rem_file); */
X }
X else if (ar_files[0][0] == '-' && ar_files[0][1] == '\0')
X {
X f_reblock++; /* Could be a pipe, be safe */
X if (f_verify)
X {
X msg ("can't verify stdin/stdout archive");
X exit (EX_ARGSBAD);
X }
X if (reading == 2)
X {
X archive = STDIN;
X msg_file = stderr;
X write_archive_to_stdout++;
X }
X else if (reading)
X archive = STDIN;
X else
X {
X archive = STDOUT;
X msg_file = stderr;
X }
X }
X else if (reading == 2 || f_verify)
X {
X archive = rmtopen (ar_files[0], O_RDWR | O_CREAT | O_BINARY, 0666);
X }
X else if (reading)
X {
X archive = rmtopen (ar_files[0], O_RDONLY | O_BINARY, 0666);
X }
X else
X {
X archive = rmtcreat (ar_files[0], 0666);
X }
X if (archive < 0)
X {
X msg_perror ("can't open %s", ar_files[0]);
X exit (EX_BADARCH);
X }
X#ifndef __MSDOS__
X if (!_isrmt (archive))
X {
X struct stat tmp_stat;
X
X fstat (archive, &tmp_stat);
X if (S_ISREG (tmp_stat.st_mode))
X {
X ar_dev = tmp_stat.st_dev;
X ar_ino = tmp_stat.st_ino;
X }
X }
X#endif
X
X#ifdef __MSDOS__
X setmode (archive, O_BINARY);
X#endif
X
X if (reading)
X {
X ar_last = ar_block; /* Set up for 1st block = # 0 */
X (void) findrec (); /* Read it in, check for EOF */
X
X if (f_volhdr)
X {
X union record *head;
X#if 0
X char *ptr;
X
X if (f_multivol)
X {
X ptr = malloc (strlen (f_volhdr) + 20);
X sprintf (ptr, "%s Volume %d", f_volhdr, 1);
X }
X else
X ptr = f_volhdr;
X#endif
X head = findrec ();
X if (!head)
X {
X msg ("Archive not labelled to match %s", f_volhdr);
X exit (EX_BADVOL);
X }
X if (re_match (label_pattern, head->header.arch_name,
X strlen (head->header.arch_name), 0, 0) < 0)
X {
X msg ("Volume mismatch! %s!=%s", f_volhdr,
X head->header.arch_name);
X exit (EX_BADVOL);
X }
X#if 0
X if (strcmp (ptr, head->header.name))
X {
X msg ("Volume mismatch! %s!=%s", ptr, head->header.name);
X exit (EX_BADVOL);
X }
X if (ptr != f_volhdr)
X free (ptr);
X#endif
X }
X }
X else if (f_volhdr)
X {
X bzero ((void *) ar_block, RECORDSIZE);
X if (f_multivol)
X sprintf (ar_block->header.arch_name, "%s Volume 1", f_volhdr);
X else
X strcpy (ar_block->header.arch_name, f_volhdr);
X current_file_name = ar_block->header.arch_name;
X ar_block->header.linkflag = LF_VOLHDR;
X to_oct (time (0), 1 + 12, ar_block->header.mtime);
X finish_header (ar_block);
X /* ar_record++; */
X }
X}
X
X
X/*
X * Remember a union record * as pointing to something that we
X * need to keep when reading onward in the file. Only one such
X * thing can be remembered at once, and it only works when reading
X * an archive.
X *
X * We calculate "offset" then add it because some compilers end up
X * adding (baserec+ar_record), doing a 9-bit shift of baserec, then
X * subtracting ar_block from that, shifting it back, losing the top 9 bits.
X */
Xvoid
Xsaverec (pointer)
X union record **pointer;
X{
X long offset;
X
X save_rec = pointer;
X offset = ar_record - ar_block;
X saved_recno = baserec + offset;
X}
X
X/*
X * Perform a write to flush the buffer.
X */
X
X/*send_buffer_to_file();
X if(new_volume) {
X deal_with_new_volume_stuff();
X send_buffer_to_file();
X }
X */
X
Xvoid
Xfl_write ()
X{
X int err;
X int copy_back;
X static long bytes_written = 0;
X
X if (f_checkpoint && !(++checkpoint % 10))
X msg ("Write checkpoint %d\n", checkpoint);
X if (tape_length && bytes_written >= tape_length * 1024)
X {
X errno = ENOSPC;
X err = 0;
X }
X else
X err = rmtwrite (archive, ar_block->charptr, (int) blocksize);
X if (err != blocksize && !f_multivol)
X writeerror (err);
X else if (f_totals)
X tot_written += blocksize;
X
X if (err > 0)
X bytes_written += err;
X if (err == blocksize)
X {
X if (f_multivol)
X {
X if (!save_name)
X {
X real_s_name[0] = '\0';
X real_s_totsize = 0;
X real_s_sizeleft = 0;
X return;
X }
X#ifdef __MSDOS__
X if (save_name[1] == ':')
X save_name += 2;
X#endif
X while (*save_name == '/')
X save_name++;
X
X strcpy (real_s_name, save_name);
X real_s_totsize = save_totsize;
X real_s_sizeleft = save_sizeleft;
X }
X return;
X }
X
X /* We're multivol Panic if we didn't get the right kind of response */
X /* ENXIO is for the UNIX PC */
X if (err < 0 && errno != ENOSPC && errno != EIO && errno != ENXIO)
X writeerror (err);
X
X /* If error indicates a short write, we just move to the next tape. */
X
X if (new_volume (0) < 0)
X return;
X bytes_written = 0;
X if (f_volhdr && real_s_name[0])
X {
X copy_back = 2;
X ar_block -= 2;
X }
X else if (f_volhdr || real_s_name[0])
X {
X copy_back = 1;
X ar_block--;
X }
X else
X copy_back = 0;
X if (f_volhdr)
X {
X bzero ((void *) ar_block, RECORDSIZE);
X sprintf (ar_block->header.arch_name, "%s Volume %d", f_volhdr, volno);
X to_oct (time (0), 1 + 12, ar_block->header.mtime);
X ar_block->header.linkflag = LF_VOLHDR;
X finish_header (ar_block);
X }
X if (real_s_name[0])
X {
X int tmp;
X
X if (f_volhdr)
X ar_block++;
X bzero ((void *) ar_block, RECORDSIZE);
X strcpy (ar_block->header.arch_name, real_s_name);
X ar_block->header.linkflag = LF_MULTIVOL;
X to_oct ((long) real_s_sizeleft, 1 + 12,
X ar_block->header.size);
X to_oct ((long) real_s_totsize - real_s_sizeleft,
X 1 + 12, ar_block->header.offset);
X tmp = f_verbose;
X f_verbose = 0;
X finish_header (ar_block);
X f_verbose = tmp;
X if (f_volhdr)
X ar_block--;
X }
X
X err = rmtwrite (archive, ar_block->charptr, (int) blocksize);
X if (err != blocksize)
X writeerror (err);
X else if (f_totals)
X tot_written += blocksize;
X
X
X bytes_written = blocksize;
X if (copy_back)
X {
X ar_block += copy_back;
X bcopy ((void *) (ar_block + blocking - copy_back),
X (void *) ar_record,
X copy_back * RECORDSIZE);
X ar_record += copy_back;
X
X if (real_s_sizeleft >= copy_back * RECORDSIZE)
X real_s_sizeleft -= copy_back * RECORDSIZE;
X else if ((real_s_sizeleft + RECORDSIZE - 1) / RECORDSIZE <= copy_back)
X real_s_name[0] = '\0';
X else
X {
X#ifdef __MSDOS__
X if (save_name[1] == ':')
X save_name += 2;
X#endif
X while (*save_name == '/')
X save_name++;
X
X strcpy (real_s_name, save_name);
X real_s_sizeleft = save_sizeleft;
X real_s_totsize = save_totsize;
X }
X copy_back = 0;
X }
X}
X
X/* Handle write errors on the archive. Write errors are always fatal */
X/* Hitting the end of a volume does not cause a write error unless the write
X* was the first block of the volume */
X
Xvoid
Xwriteerror (err)
X int err;
X{
X if (err < 0)
X {
X msg_perror ("can't write to %s", ar_files[cur_ar_file]);
X exit (EX_BADARCH);
X }
X else
X {
X msg ("only wrote %u of %u bytes to %s", err, blocksize, ar_files[cur_ar_file]);
X exit (EX_BADARCH);
X }
X}
X
X/*
X * Handle read errors on the archive.
X *
X * If the read should be retried, readerror() returns to the caller.
X */
Xvoid
Xreaderror ()
X{
X# define READ_ERROR_MAX 10
X
X read_error_flag++; /* Tell callers */
X
X msg_perror ("read error on %s", ar_files[cur_ar_file]);
X
X if (baserec == 0)
X {
X /* First block of tape. Probably stupidity error */
X exit (EX_BADARCH);
X }
X
X /*
X * Read error in mid archive. We retry up to READ_ERROR_MAX times
X * and then give up on reading the archive. We set read_error_flag
X * for our callers, so they can cope if they want.
X */
X if (r_error_count++ > READ_ERROR_MAX)
X {
X msg ("Too many errors, quitting.");
X exit (EX_BADARCH);
X }
X return;
X}
X
X
X/*
X * Perform a read to flush the buffer.
X */
Xvoid
Xfl_read ()
X{
X int err; /* Result from system call */
X int left; /* Bytes left */
X char *more; /* Pointer to next byte to read */
X
X if (f_checkpoint && !(++checkpoint % 10))
X msg ("Read checkpoint %d\n", checkpoint);
X
X /*
X * Clear the count of errors. This only applies to a single
X * call to fl_read. We leave read_error_flag alone; it is
X * only turned off by higher level software.
X */
X r_error_count = 0; /* Clear error count */
X
X /*
X * If we are about to wipe out a record that
X * somebody needs to keep, copy it out to a holding
X * area and adjust somebody's pointer to it.
X */
X if (save_rec &&
X *save_rec >= ar_record &&
X *save_rec < ar_last)
X {
X record_save_area = **save_rec;
X *save_rec = &record_save_area;
X }
X if (write_archive_to_stdout && baserec != 0)
X {
X err = rmtwrite (1, ar_block->charptr, blocksize);
X if (err != blocksize)
X writeerror (err);
X }
X if (f_multivol)
X {
X if (save_name)
X {
X if (save_name != real_s_name)
X {
X#ifdef __MSDOS__
X if (save_name[1] == ':')
X save_name += 2;
X#endif
X while (*save_name == '/')
X save_name++;
X
X strcpy (real_s_name, save_name);
X save_name = real_s_name;
X }
X real_s_totsize = save_totsize;
X real_s_sizeleft = save_sizeleft;
X
X }
X else
X {
X real_s_name[0] = '\0';
X real_s_totsize = 0;
X real_s_sizeleft = 0;
X }
X }
X
Xerror_loop:
X err = rmtread (archive, ar_block->charptr, (int) blocksize);
X if (err == blocksize)
X return;
X
X if ((err == 0 || (err < 0 && errno == ENOSPC) || (err > 0 && !f_reblock)) && f_multivol)
X {
X union record *head;
X
X try_volume:
X if (new_volume ((cmd_mode == CMD_APPEND || cmd_mode == CMD_CAT || cmd_mode == CMD_UPDATE) ? 2 : 1) < 0)
X return;
X vol_error:
X err = rmtread (archive, ar_block->charptr, (int) blocksize);
X if (err < 0)
X {
X readerror ();
X goto vol_error;
X }
X if (err != blocksize)
X goto short_read;
X
X head = ar_block;
X
X if (head->header.linkflag == LF_VOLHDR)
X {
X if (f_volhdr)
X {
X#if 0
X char *ptr;
X
X ptr = (char *) malloc (strlen (f_volhdr) + 20);
X sprintf (ptr, "%s Volume %d", f_volhdr, volno);
X#endif
X if (re_match (label_pattern, head->header.arch_name,
X strlen (head->header.arch_name),
X 0, 0) < 0)
X {
X msg ("Volume mismatch! %s!=%s", f_volhdr,
X head->header.arch_name);
X --volno;
X --global_volno;
X goto try_volume;
X }
X
X#if 0
X if (strcmp (ptr, head->header.name))
X {
X msg ("Volume mismatch! %s!=%s", ptr, head->header.name);
X --volno;
X --global_volno;
X free (ptr);
X goto try_volume;
X }
X free (ptr);
X#endif
X }
X if (f_verbose)
X fprintf (msg_file, "Reading %s\n", head->header.arch_name);
X head++;
X }
X else if (f_volhdr)
X {
X msg ("Warning: No volume header!");
X }
X
X if (real_s_name[0])
X {
X long from_oct ();
X
X if (head->header.linkflag != LF_MULTIVOL || strcmp (head->header.arch_name, real_s_name))
X {
X msg ("%s is not continued on this volume!", real_s_name);
X --volno;
X --global_volno;
X goto try_volume;
X }
X if (real_s_totsize != from_oct (1 + 12, head->header.size) + from_oct (1 + 12, head->header.offset))
X {
X msg ("%s is the wrong size (%ld!=%ld+%ld)",
X head->header.arch_name, save_totsize,
X from_oct (1 + 12, head->header.size),
X from_oct (1 + 12, head->header.offset));
X --volno;
X --global_volno;
X goto try_volume;
X }
X if (real_s_totsize - real_s_sizeleft != from_oct (1 + 12, head->header.offset))
X {
X msg ("This volume is out of sequence");
X --volno;
X --global_volno;
X goto try_volume;
X }
X head++;
X }
X ar_record = head;
X return;
X }
X else if (err < 0)
X {
X readerror ();
X goto error_loop; /* Try again */
X }
X
Xshort_read:
X more = ar_block->charptr + err;
X left = blocksize - err;
X
Xagain:
X if (0 == (((unsigned) left) % RECORDSIZE))
X {
X /* FIXME, for size=0, multi vol support */
X /* On the first block, warn about the problem */
X if (!f_reblock && baserec == 0 && f_verbose && err > 0)
X {
X /* msg("Blocksize = %d record%s",
X err / RECORDSIZE, (err > RECORDSIZE)? "s": "");*/
X msg ("Blocksize = %d records", err / RECORDSIZE);
X }
X ar_last = ar_block + ((unsigned) (blocksize - left)) / RECORDSIZE;
X return;
X }
X if (f_reblock)
X {
X /*
X * User warned us about this. Fix up.
X */
X if (left > 0)
X {
X error2loop:
X err = rmtread (archive, more, (int) left);
X if (err < 0)
X {
X readerror ();
X goto error2loop; /* Try again */
X }
X if (err == 0)
X {
X msg ("archive %s EOF not on block boundary", ar_files[cur_ar_file]);
X exit (EX_BADARCH);
X }
X left -= err;
X more += err;
X goto again;
X }
X }
X else
X {
X msg ("only read %d bytes from archive %s", err, ar_files[cur_ar_file]);
X exit (EX_BADARCH);
X }
X}
X
X
X/*
X * Flush the current buffer to/from the archive.
X */
Xvoid
Xflush_archive ()
X{
X int c;
X
X baserec += ar_last - ar_block;/* Keep track of block #s */
X ar_record = ar_block; /* Restore pointer to start */
X ar_last = ar_block + blocking;/* Restore pointer to end */
X
X if (ar_reading)
X {
X if (time_to_start_writing)
X {
X time_to_start_writing = 0;
X ar_reading = 0;
X
X if (file_to_switch_to >= 0)
X {
X if ((c = rmtclose (archive)) < 0)
X msg_perror ("Warning: can't close %s(%d,%d)", ar_files[cur_ar_file], archive, c);
X
X archive = file_to_switch_to;
X }
X else
X (void) backspace_output ();
X fl_write ();
X }
X else
X fl_read ();
X }
X else
X {
X fl_write ();
X }
X}
X
X/* Backspace the archive descriptor by one blocks worth.
X If its a tape, MTIOCTOP will work. If its something else,
X we try to seek on it. If we can't seek, we lose! */
Xint
Xbackspace_output ()
X{
X long cur;
X /* int er; */
X extern char *output_start;
X
X#ifdef MTIOCTOP
X struct mtop t;
X
X t.mt_op = MTBSR;
X t.mt_count = 1;
X if ((rmtioctl (archive, MTIOCTOP, &t)) >= 0)
X return 1;
X if (errno == EIO && (rmtioctl (archive, MTIOCTOP, &t)) >= 0)
X return 1;
X#endif
X
X cur = rmtlseek (archive, 0L, 1);
X cur -= blocksize;
X /* Seek back to the beginning of this block and
X start writing there. */
X
X if (rmtlseek (archive, cur, 0) != cur)
X {
X /* Lseek failed. Try a different method */
X msg ("Couldn't backspace archive file. It may be unreadable without -i.");
X /* Replace the first part of the block with nulls */
X if (ar_block->charptr != output_start)
X bzero (ar_block->charptr, output_start - ar_block->charptr);
X return 2;
X }
X return 3;
X}
X
X
X/*
X * Close the archive file.
X */
Xvoid
Xclose_archive ()
X{
X int child;
X int status;
X int c;
X
X if (time_to_start_writing || !ar_reading)
X flush_archive ();
X if (cmd_mode == CMD_DELETE)
X {
X off_t pos;
X
X pos = rmtlseek (archive, 0L, 1);
X#ifndef __MSDOS__
X (void) ftruncate (archive, pos);
X#else
X (void) rmtwrite (archive, "", 0);
X#endif
X }
X if (f_verify)
X verify_volume ();
X
X if ((c = rmtclose (archive)) < 0)
X msg_perror ("Warning: can't close %s(%d,%d)", ar_files[cur_ar_file], archive, c);
X
X#ifndef __MSDOS__
X if (childpid)
X {
X /*
X * Loop waiting for the right child to die, or for
X * no more kids.
X */
X while (((child = wait (&status)) != childpid) && child != -1)
X ;
X
X if (child != -1)
X {
X if (WIFSIGNALED (status))
X {
X /* SIGPIPE is OK, everything else is a problem. */
X if (WTERMSIG (status) != SIGPIPE)
X msg ("child died with signal %d%s", WTERMSIG (status),
X WIFCOREDUMPED (status) ? " (core dumped)" : "");
X }
X else
X {
X /* Child voluntarily terminated -- but why? */
X if (WEXITSTATUS (status) == MAGIC_STAT)
X {
X exit (EX_SYSTEM); /* Child had trouble */
X }
X if (WEXITSTATUS (status) == (SIGPIPE + 128))
X {
X /*
X * /bin/sh returns this if its child
X * dies with SIGPIPE. 'Sok.
X */
X /* Do nothing. */
X }
X else if (WEXITSTATUS (status))
X msg ("child returned status %d",
X WEXITSTATUS (status));
X }
X }
X }
X#endif /* __MSDOS__ */
X}
X
X
X#ifdef DONTDEF
X/*
X * Message management.
X *
X * anno writes a message prefix on stream (eg stdout, stderr).
X *
X * The specified prefix is normally output followed by a colon and a space.
X * However, if other command line options are set, more output can come
X * out, such as the record # within the archive.
X *
X * If the specified prefix is NULL, no output is produced unless the
X * command line option(s) are set.
X *
X * If the third argument is 1, the "saved" record # is used; if 0, the
X * "current" record # is used.
X */
Xvoid
Xanno (stream, prefix, savedp)
X FILE *stream;
X char *prefix;
X int savedp;
X{
X# define MAXANNO 50
X char buffer[MAXANNO]; /* Holds annorecment */
X# define ANNOWIDTH 13
X int space;
X long offset;
X int save_e;
X
X save_e = errno;
X /* Make sure previous output gets out in sequence */
X if (stream == stderr)
X fflush (stdout);
X if (f_sayblock)
X {
X if (prefix)
X {
X fputs (prefix, stream);
X putc (' ', stream);
X }
X offset = ar_record - ar_block;
X (void) sprintf (buffer, "rec %d: ",
X savedp ? saved_recno :
X baserec + offset);
X fputs (buffer, stream);
X space = ANNOWIDTH - strlen (buffer);
X if (space > 0)
X {
X fprintf (stream, "%*s", space, "");
X }
X }
X else if (prefix)
X {
X fputs (prefix, stream);
X fputs (": ", stream);
X }
X errno = save_e;
X}
X
X#endif
X
X/* Called to initialize the global volume number. */
Xvoid
Xinit_volume_number ()
X{
X FILE *vf;
X
X vf = fopen (f_volno_file, "r");
X if (!vf && errno != ENOENT)
X msg_perror ("%s", f_volno_file);
X
X if (vf)
X {
X fscanf (vf, "%d", &global_volno);
X fclose (vf);
X }
X}
X
X/* Called to write out the closing global volume number. */
Xvoid
Xcloseout_volume_number ()
X{
X FILE *vf;
X
X vf = fopen (f_volno_file, "w");
X if (!vf)
X msg_perror ("%s", f_volno_file);
X else
X {
X fprintf (vf, "%d\n", global_volno);
X fclose (vf);
X }
X}
X
X/* We've hit the end of the old volume. Close it and open the next one */
X/* Values for type: 0: writing 1: reading 2: updating */
Xint
Xnew_volume (type)
X int type;
X{
X int c;
X char inbuf[80];
X char *p;
X static FILE *read_file = 0;
X extern int now_verifying;
X extern char TTY_NAME[];
X static int looped = 0;
X
X if (!read_file && !f_run_script_at_end)
X read_file = (archive == 0) ? fopen (TTY_NAME, "r") : stdin;
X
X if (now_verifying)
X return -1;
X if (f_verify)
X verify_volume ();
X if ((c = rmtclose (archive)) < 0)
X msg_perror ("Warning: can't close %s(%d,%d)", ar_files[cur_ar_file], archive, c);
X
X global_volno++;
X volno++;
X cur_ar_file++;
X if (cur_ar_file == n_ar_files)
X {
X cur_ar_file = 0;
X looped = 1;
X }
X
Xtryagain:
X if (looped)
X {
X /* We have to prompt from now on. */
X if (f_run_script_at_end)
X {
X closeout_volume_number ();
X system (info_script);
X }
X else
X for (;;)
X {
X fprintf (msg_file, "\007Prepare volume #%d for %s and hit return: ", global_volno, ar_files[cur_ar_file]);
X fflush (msg_file);
X if (fgets (inbuf, sizeof (inbuf), read_file) == 0)
X {
X fprintf (msg_file, "EOF? What does that mean?");
X if (cmd_mode != CMD_EXTRACT && cmd_mode != CMD_LIST && cmd_mode != CMD_DIFF)
X msg ("Warning: Archive is INCOMPLETE!");
X exit (EX_BADARCH);
X }
X if (inbuf[0] == '\n' || inbuf[0] == 'y' || inbuf[0] == 'Y')
X break;
X
X switch (inbuf[0])
X {
X case '?':
X {
X fprintf (msg_file, "\
X n [name] Give a new filename for the next (and subsequent) volume(s)\n\
X q Abort tar\n\
X ! Spawn a subshell\n\
X ? Print this list\n");
X }
X break;
X
X case 'q': /* Quit */
X fprintf (msg_file, "No new volume; exiting.\n");
X if (cmd_mode != CMD_EXTRACT && cmd_mode != CMD_LIST && cmd_mode != CMD_DIFF)
X msg ("Warning: Archive is INCOMPLETE!");
X exit (EX_BADARCH);
X
X case 'n': /* Get new file name */
X {
X char *q, *r;
X static char *old_name;
X
X for (q = &inbuf[1]; *q == ' ' || *q == '\t'; q++)
X ;
X for (r = q; *r; r++)
X if (*r == '\n')
X *r = '\0';
X old_name = p = (char *) malloc ((unsigned) (strlen (q) + 2));
X if (p == 0)
X {
X msg ("Can't allocate memory for name");
X exit (EX_SYSTEM);
X }
X (void) strcpy (p, q);
X ar_files[cur_ar_file] = p;
X }
X break;
X
X case '!':
X#ifdef __MSDOS__
X spawnl (P_WAIT, getenv ("COMSPEC"), "-", 0);
X#else
X /* JF this needs work! */
X switch (fork ())
X {
X case -1:
X msg_perror ("can't fork!");
X break;
X case 0:
X p = getenv ("SHELL");
X if (p == 0)
X p = "/bin/sh";
X execlp (p, "-sh", "-i", 0);
X msg_perror ("can't exec a shell %s", p);
X _exit (55);
X default:
X wait (0);
X break;
X }
X#endif
X break;
X }
X }
X }
X
X
X if (type == 2 || f_verify)
X archive = rmtopen (ar_files[cur_ar_file], O_RDWR | O_CREAT, 0666);
X else if (type == 1)
X archive = rmtopen (ar_files[cur_ar_file], O_RDONLY, 0666);
X else if (type == 0)
X archive = rmtcreat (ar_files[cur_ar_file], 0666);
X else
X archive = -1;
X
X if (archive < 0)
X {
X msg_perror ("can't open %s", ar_files[cur_ar_file]);
X goto tryagain;
X }
X#ifdef __MSDOS__
X setmode (archive, O_BINARY);
X#endif
X return 0;
X}
X
X/* this is a useless function that takes a buffer returned by wantbytes
X and does nothing with it. If the function called by wantbytes returns
X an error indicator (non-zero), this function is called for the rest of
X the file.
X */
Xint
Xno_op (size, data)
X int size;
X char *data;
X{
X return 0;
X}
X
X/* Some other routine wants SIZE bytes in the archive. For each chunk of
X the archive, call FUNC with the size of the chunk, and the address of
X the chunk it can work with.
X */
Xint
Xwantbytes (size, func)
X long size;
X int (*func) ();
X{
X char *data;
X long data_size;
X
X while (size)
X {
X data = findrec ()->charptr;
X if (data == NULL)
X { /* Check it... */
X msg ("Unexpected EOF on archive file");
X return -1;
X }
X data_size = endofrecs ()->charptr - data;
X if (data_size > size)
X data_size = size;
X if ((*func) (data_size, data))
X func = no_op;
X userec ((union record *) (data + data_size - 1));
X size -= data_size;
X }
X return 0;
X}
END_OF_FILE
if test 35194 -ne `wc -c <'buffer.c'`; then
echo shar: \"'buffer.c'\" unpacked with wrong size!
fi
# end of 'buffer.c'
fi
if test -f 'getoldopt.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'getoldopt.c'\"
else
echo shar: Extracting \"'getoldopt.c'\" \(2310 characters\)
sed "s/^X//" >'getoldopt.c' <<'END_OF_FILE'
X/* Replacement for getopt() that can be used by tar.
X Copyright (C) 1988 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/*
X * Plug-compatible replacement for getopt() for parsing tar-like
X * arguments. If the first argument begins with "-", it uses getopt;
X * otherwise, it uses the old rules used by tar, dump, and ps.
X *
X * Written 25 August 1985 by John Gilmore (ihnp4!hoptoad!gnu)
X */
X
X#include <stdio.h>
X#include "getopt.h"
X#include "tar.h" /* For msg() declaration if STDC_MSG. */
X#include <sys/types.h>
X#include "port.h"
X
Xint
Xgetoldopt (argc, argv, optstring, long_options, opt_index)
X int argc;
X char **argv;
X char *optstring;
X struct option *long_options;
X int *opt_index;
X{
X extern char *optarg; /* Points to next arg */
X extern int optind; /* Global argv index */
X static char *key; /* Points to next keyletter */
X static char use_getopt; /* !=0 if argv[1][0] was '-' */
X char c;
X char *place;
X
X optarg = NULL;
X
X if (key == NULL)
X { /* First time */
X if (argc < 2)
X return EOF;
X key = argv[1];
X if ((*key == '-') || (*key == '+'))
X use_getopt++;
X else
X optind = 2;
X }
X
X if (use_getopt)
X return getopt_long (argc, argv, optstring,
X long_options, opt_index);
X
X c = *key++;
X if (c == '\0')
X {
X key--;
X return EOF;
X }
X place = index (optstring, c);
X
X if (place == NULL || c == ':')
X {
X msg ("unknown option %c", c);
X return ('?');
X }
X
X place++;
X if (*place == ':')
X {
X if (optind < argc)
X {
X optarg = argv[optind];
X optind++;
X }
X else
X {
X msg ("%c argument missing", c);
X return ('?');
X }
X }
X
X return (c);
X}
END_OF_FILE
if test 2310 -ne `wc -c <'getoldopt.c'`; then
echo shar: \"'getoldopt.c'\" unpacked with wrong size!
fi
# end of 'getoldopt.c'
fi
if test -f 'update.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'update.c'\"
else
echo shar: Extracting \"'update.c'\" \(13900 characters\)
sed "s/^X//" >'update.c' <<'END_OF_FILE'
X/* Update a tar archive.
X Copyright (C) 1988, 1992 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* JF implement the 'r' 'u' and 'A' options for tar. */
X/* The 'A' option is my own invention: It means that the file-names are
X tar files, and they should simply be appended to the end of the archive.
X No attempt is made to block the reads from the args; if they're on raw
X tape or something like that, it'll probably lose. . . */
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <errno.h>
X#ifndef STDC_HEADERS
Xextern int errno;
X#endif
X
X#ifdef HAVE_SYS_MTIO_H
X#include <sys/ioctl.h>
X#include <sys/mtio.h>
X#endif
X
X#ifdef BSD42
X#include <sys/file.h>
X#else
X#ifndef V7
X#include <fcntl.h>
X#endif
X#endif
X
X#ifndef __MSDOS__
X#include <pwd.h>
X#include <grp.h>
X#endif
X
X#define STDIN 0
X#define STDOUT 1
X
X#include "tar.h"
X#include "port.h"
X#include "rmt.h"
X
Xint time_to_start_writing = 0; /* We've hit the end of the old stuff,
X and its time to start writing new stuff
X to the tape. This involves seeking
X back one block and re-writing the current
X block (which has been changed). */
X
Xchar *output_start; /* Pointer to where we started to write in
X the first block we write out. This is used
X if we can't backspace the output and have
X to null out the first part of the block */
X
Xextern void skip_file ();
Xextern void skip_extended_headers ();
X
Xextern union record *head;
Xextern struct stat hstat;
X
Xvoid append_file ();
Xvoid close_archive ();
Xint confirm ();
Xvoid decode_header ();
Xvoid fl_read ();
Xvoid fl_write ();
Xvoid flush_archive ();
Xint move_arch ();
Xstruct name *name_scan ();
Xchar *name_from_list ();
Xvoid name_expand ();
Xvoid name_gather ();
Xvoid names_notfound ();
Xvoid open_archive ();
Xint read_header ();
Xvoid reset_eof ();
Xvoid write_block ();
Xvoid write_eot ();
X
X/* Implement the 'r' (add files to end of archive), and 'u' (add files to
X end of archive if they arent there, or are more up to date than the
X version in the archive.) commands.*/
Xvoid
Xupdate_archive ()
X{
X int found_end = 0;
X int status = 3;
X int prev_status;
X char *p;
X struct name *name;
X extern void dump_file ();
X
X name_gather ();
X if (cmd_mode == CMD_UPDATE)
X name_expand ();
X open_archive (2); /* Open for updating */
X
X do
X {
X prev_status = status;
X status = read_header ();
X switch (status)
X {
X case EOF:
X found_end = 1;
X break;
X
X case 0: /* A bad record */
X userec (head);
X switch (prev_status)
X {
X case 3:
X msg ("This doesn't look like a tar archive.");
X /* FALL THROUGH */
X case 2:
X case 1:
X msg ("Skipping to next header");
X case 0:
X break;
X }
X break;
X
X /* A good record */
X case 1:
X /* printf("File %s\n",head->header.name); */
X /* head->header.name[NAMSIZ-1]='\0'; */
X if (cmd_mode == CMD_UPDATE && (name = name_scan (current_file_name)))
X {
X
X /* struct stat hstat; */
X struct stat nstat;
X int head_standard;
X
X decode_header (head, &hstat, &head_standard, 0);
X if (stat (current_file_name, &nstat) < 0)
X {
X msg_perror ("can't stat %s:", current_file_name);
X }
X else
X {
X if (hstat.st_mtime >= nstat.st_mtime)
X name->found++;
X }
X }
X userec (head);
X if (head->header.isextended)
X skip_extended_headers ();
X skip_file ((long) hstat.st_size);
X break;
X
X case 2:
X ar_record = head;
X found_end = 1;
X break;
X }
X }
X while (!found_end);
X
X reset_eof ();
X time_to_start_writing = 1;
X output_start = ar_record->charptr;
X
X while (p = name_from_list ())
X {
X if (f_confirm && !confirm ("add", p))
X continue;
X if (cmd_mode == CMD_CAT)
X append_file (p);
X else
X dump_file (p, -1, 1);
X }
X
X write_eot ();
X close_archive ();
X names_notfound ();
X}
X
X/* Catenate file p to the archive without creating a header for it. It had
X better be a tar file or the archive is screwed */
X
Xvoid
Xappend_file (p)
X char *p;
X{
X int fd;
X struct stat statbuf;
X long bytes_left;
X union record *start;
X long bufsiz, count;
X
X if (0 != stat (p, &statbuf) || (fd = open (p, O_RDONLY | O_BINARY)) < 0)
X {
X msg_perror ("can't open file %s", p);
X errors++;
X return;
X }
X
X bytes_left = statbuf.st_size;
X
X while (bytes_left > 0)
X {
X start = findrec ();
X bufsiz = endofrecs ()->charptr - start->charptr;
X if (bytes_left < bufsiz)
X {
X bufsiz = bytes_left;
X count = bufsiz % RECORDSIZE;
X if (count)
X bzero (start->charptr + bytes_left, (int) (RECORDSIZE - count));
X }
X count = read (fd, start->charptr, bufsiz);
X if (count < 0)
X {
X msg_perror ("read error at byte %ld reading %d bytes in file %s", statbuf.st_size - bytes_left, bufsiz, p);
X exit (EX_ARGSBAD); /* FOO */
X }
X bytes_left -= count;
X userec (start + (count - 1) / RECORDSIZE);
X if (count != bufsiz)
X {
X msg ("%s: file shrunk by %d bytes, yark!", p, bytes_left);
X abort ();
X }
X }
X (void) close (fd);
X}
X
X#ifdef DONTDEF
Xbprint (fp, buf, num)
X FILE *fp;
X char *buf;
X{
X int c;
X
X if (num == 0 || num == -1)
X return;
X fputs (" '", fp);
X while (num--)
X {
X c = *buf++;
X if (c == '\\')
X fputs ("\\\\", fp);
X else if (c >= ' ' && c <= '~')
X putc (c, fp);
X else
X switch (c)
X {
X case '\n':
X fputs ("\\n", fp);
X break;
X case '\r':
X fputs ("\\r", fp);
X break;
X case '\b':
X fputs ("\\b", fp);
X break;
X case '\0':
X /* fputs("\\-",fp); */
X break;
X default:
X fprintf (fp, "\\%03o", c);
X break;
X }
X }
X fputs ("'\n", fp);
X}
X
X#endif
X
Xint number_of_blocks_read = 0;
X
Xint number_of_new_records = 0;
Xint number_of_records_needed = 0;
X
Xunion record *new_block = 0;
Xunion record *save_block = 0;
X
Xvoid
Xjunk_archive ()
X{
X int found_stuff = 0;
X int status = 3;
X int prev_status;
X struct name *name;
X
X /* int dummy_head; */
X int number_of_records_to_skip = 0;
X int number_of_records_to_keep = 0;
X int number_of_kept_records_in_block;
X int sub_status;
X extern int write_archive_to_stdout;
X
X /* fprintf(stderr,"Junk files\n"); */
X name_gather ();
X open_archive (2);
X
X while (!found_stuff)
X {
X prev_status = status;
X status = read_header ();
X switch (status)
X {
X case EOF:
X found_stuff = 1;
X break;
X
X case 0:
X userec (head);
X switch (prev_status)
X {
X case 3:
X msg ("This doesn't look like a tar archive.");
X /* FALL THROUGH */
X case 2:
X case 1:
X msg ("Skipping to next header");
X /* FALL THROUGH */
X case 0:
X break;
X }
X break;
X
X case 1:
X /* head->header.name[NAMSIZ-1] = '\0'; */
X /* fprintf(stderr,"file %s\n",head->header.name); */
X if ((name = name_scan (current_file_name)) == (struct name *) 0)
X {
X userec (head);
X /* fprintf(stderr,"Skip %ld\n",(long)(hstat.st_size)); */
X if (head->header.isextended)
X skip_extended_headers ();
X skip_file ((long) (hstat.st_size));
X break;
X }
X name->found = 1;
X found_stuff = 2;
X break;
X
X case 2:
X found_stuff = 1;
X break;
X }
X }
X /* fprintf(stderr,"Out of first loop\n"); */
X
X if (found_stuff != 2)
X {
X write_eot ();
X close_archive ();
X names_notfound ();
X return;
X }
X
X if (write_archive_to_stdout)
X write_archive_to_stdout = 0;
X new_block = (union record *) malloc (blocksize);
X if (new_block == 0)
X {
X msg ("Can't allocate secondary block of %d bytes", blocksize);
X exit (EX_SYSTEM);
X }
X
X /* Save away records before this one in this block */
X number_of_new_records = ar_record - ar_block;
X number_of_records_needed = blocking - number_of_new_records;
X if (number_of_new_records)
X bcopy ((void *) ar_block, (void *) new_block, (number_of_new_records) * RECORDSIZE);
X
X /* fprintf(stderr,"Saved %d recs, need %d more\n",number_of_new_records,number_of_records_needed); */
X userec (head);
X if (head->header.isextended)
X skip_extended_headers ();
X skip_file ((long) (hstat.st_size));
X found_stuff = 0;
X /* goto flush_file; */
X
X for (;;)
X {
X /* Fill in a block */
X /* another_file: */
X if (ar_record == ar_last)
X {
X /* fprintf(stderr,"New block\n"); */
X flush_archive ();
X number_of_blocks_read++;
X }
X sub_status = read_header ();
X /* fprintf(stderr,"Header type %d\n",sub_status); */
X
X if (sub_status == 2 && f_ignorez)
X {
X userec (head);
X continue;
X }
X if (sub_status == EOF || sub_status == 2)
X {
X found_stuff = 1;
X bzero (new_block[number_of_new_records].charptr, RECORDSIZE * number_of_records_needed);
X number_of_new_records += number_of_records_needed;
X number_of_records_needed = 0;
X write_block (0);
X break;
X }
X
X if (sub_status == 0)
X {
X msg ("Deleting non-header from archive.");
X userec (head);
X continue;
X }
X
X /* Found another header. Yipee! */
X /* head->header.name[NAMSIZ-1] = '\0'; */
X /* fprintf(stderr,"File %s ",head->header.name); */
X if (name = name_scan (current_file_name))
X {
X name->found = 1;
X /* fprintf(stderr,"Flush it\n"); */
X /* flush_file: */
X /* decode_header(head,&hstat,&dummy_head,0); */
X userec (head);
X number_of_records_to_skip = (hstat.st_size + RECORDSIZE - 1) / RECORDSIZE;
X /* fprintf(stderr,"Flushing %d recs from %s\n",number_of_records_to_skip,head->header.name); */
X
X while (ar_last - ar_record <= number_of_records_to_skip)
X {
X
X /* fprintf(stderr,"Block: %d <= %d ",ar_last-ar_record,number_of_records_to_skip); */
X number_of_records_to_skip -= (ar_last - ar_record);
X flush_archive ();
X number_of_blocks_read++;
X /* fprintf(stderr,"Block %d left\n",number_of_records_to_skip); */
X }
X ar_record += number_of_records_to_skip;
X /* fprintf(stderr,"Final %d\n",number_of_records_to_skip); */
X number_of_records_to_skip = 0;
X continue;
X }
X
X /* copy_header: */
X new_block[number_of_new_records] = *head;
X number_of_new_records++;
X number_of_records_needed--;
X number_of_records_to_keep = (hstat.st_size + RECORDSIZE - 1) / RECORDSIZE;
X userec (head);
X if (number_of_records_needed == 0)
X write_block (1);
X /* copy_data: */
X number_of_kept_records_in_block = ar_last - ar_record;
X if (number_of_kept_records_in_block > number_of_records_to_keep)
X number_of_kept_records_in_block = number_of_records_to_keep;
X
X /* fprintf(stderr,"Need %d kept_in %d keep %d\n",blocking,number_of_kept_records_in_block,number_of_records_to_keep); */
X
X while (number_of_records_to_keep)
X {
X int n;
X
X if (ar_record == ar_last)
X {
X /* fprintf(stderr,"Flush. . .\n"); */
X fl_read ();
X number_of_blocks_read++;
X ar_record = ar_block;
X number_of_kept_records_in_block = blocking;
X if (number_of_kept_records_in_block > number_of_records_to_keep)
X number_of_kept_records_in_block = number_of_records_to_keep;
X }
X n = number_of_kept_records_in_block;
X if (n > number_of_records_needed)
X n = number_of_records_needed;
X
X /* fprintf(stderr,"Copying %d\n",n); */
X bcopy ((void *) ar_record, (void *) (new_block + number_of_new_records), n * RECORDSIZE);
X number_of_new_records += n;
X number_of_records_needed -= n;
X ar_record += n;
X number_of_records_to_keep -= n;
X number_of_kept_records_in_block -= n;
X /* fprintf(stderr,"Now new %d need %d keep %d keep_in %d rec %d/%d\n",
X number_of_new_records,number_of_records_needed,number_of_records_to_keep,
X number_of_kept_records_in_block,ar_record-ar_block,ar_last-ar_block); */
X
X if (number_of_records_needed == 0)
X {
X write_block (1);
X }
X }
X }
X
X write_eot ();
X close_archive ();
X names_notfound ();
X}
X
Xvoid
Xwrite_block (f)
X int f;
X{
X /* fprintf(stderr,"Write block\n"); */
X /* We've filled out a block. Write it out. */
X
X /* Backspace back to where we started. . . */
X if (archive != STDIN)
X (void) move_arch (-(number_of_blocks_read + 1));
X
X save_block = ar_block;
X ar_block = new_block;
X
X if (archive == STDIN)
X archive = STDOUT;
X fl_write ();
X
X if (archive == STDOUT)
X archive = STDIN;
X ar_block = save_block;
X
X if (f)
X {
X /* Move the tape head back to where we were */
X if (archive != STDIN)
X (void) move_arch (number_of_blocks_read);
X number_of_blocks_read--;
X }
X
X number_of_records_needed = blocking;
X number_of_new_records = 0;
X}
X
X/* Move archive descriptor by n blocks worth. If n is positive we move
X forward, else we move negative. If its a tape, MTIOCTOP had better
X work. If its something else, we try to seek on it. If we can't
X seek, we lose! */
Xint
Xmove_arch (n)
X int n;
X{
X long cur;
X
X#ifdef MTIOCTOP
X struct mtop t;
X int er;
X
X if (n > 0)
X {
X t.mt_op = MTFSR;
X t.mt_count = n;
X }
X else
X {
X t.mt_op = MTBSR;
X t.mt_count = -n;
X }
X if ((er = rmtioctl (archive, MTIOCTOP, &t)) >= 0)
X return 1;
X if (errno == EIO && (er = rmtioctl (archive, MTIOCTOP, &t)) >= 0)
X return 1;
X#endif
X
X cur = rmtlseek (archive, 0L, 1);
X cur += blocksize * n;
X
X /* fprintf(stderr,"Fore to %x\n",cur); */
X if (rmtlseek (archive, cur, 0) != cur)
X {
X /* Lseek failed. Try a different method */
X msg ("Couldn't re-position archive file.");
X exit (EX_BADARCH);
X }
X return 3;
X}
END_OF_FILE
if test 13900 -ne `wc -c <'update.c'`; then
echo shar: \"'update.c'\" unpacked with wrong size!
fi
# end of 'update.c'
fi
if test -f 'gnu.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'gnu.c'\"
else
echo shar: Extracting \"'gnu.c'\" \(13964 characters\)
sed "s/^X//" >'gnu.c' <<'END_OF_FILE'
X/* GNU dump extensions to tar.
X Copyright (C) 1988, 1992, 1993 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <ctype.h>
X#include <errno.h>
X#ifndef STDC_HEADERS
Xextern int errno;
X#endif
X#include <time.h>
Xtime_t time ();
X
X#include "tar.h"
X#include "port.h"
X
X#ifndef S_ISLNK
X#define lstat stat
X#endif
X
Xextern time_t new_time;
Xextern FILE *msg_file;
X
Xvoid addname ();
Xint check_exclude ();
Xextern PTR ck_malloc ();
Xextern PTR ck_realloc ();
Xint confirm ();
Xextern PTR init_buffer ();
Xextern char *get_buffer ();
Xint is_dot_or_dotdot ();
Xextern void add_buffer ();
Xextern void flush_buffer ();
Xvoid name_gather ();
Xint recursively_delete ();
Xvoid skip_file ();
Xchar *un_quote_string ();
X
Xextern char *new_name ();
X
Xstatic void add_dir_name ();
X
Xstruct dirname
X {
X struct dirname *next;
X char *name;
X char *dir_text;
X int dev;
X int ino;
X int allnew;
X };
Xstatic struct dirname *dir_list;
Xstatic time_t this_time;
X
Xvoid
Xadd_dir (name, dev, ino, text)
X char *name;
X char *text;
X dev_t dev;
X ino_t ino;
X{
X struct dirname *dp;
X
X dp = (struct dirname *) ck_malloc (sizeof (struct dirname));
X if (!dp)
X abort ();
X dp->next = dir_list;
X dir_list = dp;
X dp->dev = dev;
X dp->ino = ino;
X dp->name = ck_malloc (strlen (name) + 1);
X strcpy (dp->name, name);
X dp->dir_text = text;
X dp->allnew = 0;
X}
X
Xvoid
Xread_dir_file ()
X{
X int dev;
X int ino;
X char *strp;
X FILE *fp;
X char buf[512];
X static char *path = 0;
X
X if (path == 0)
X path = ck_malloc (PATH_MAX);
X time (&this_time);
X if (gnu_dumpfile[0] != '/')
X {
X#if defined(__MSDOS__) || defined(HAVE_GETCWD) || defined(_POSIX_VERSION)
X if (!getcwd (path, PATH_MAX))
X {
X msg ("Couldn't get current directory.");
X exit (EX_SYSTEM);
X }
X#else
X char *getwd ();
X
X if (!getwd (path))
X {
X msg ("Couldn't get current directory: %s", path);
X exit (EX_SYSTEM);
X }
X#endif
X /* If this doesn't fit, we're in serious trouble */
X strcat (path, "/");
X strcat (path, gnu_dumpfile);
X gnu_dumpfile = path;
X }
X fp = fopen (gnu_dumpfile, "r");
X if (fp == 0 && errno != ENOENT)
X {
X msg_perror ("Can't open %s", gnu_dumpfile);
X return;
X }
X if (!fp)
X return;
X fgets (buf, sizeof (buf), fp);
X if (!f_new_files)
X {
X f_new_files++;
X new_time = atol (buf);
X }
X while (fgets (buf, sizeof (buf), fp))
X {
X strp = &buf[strlen (buf)];
X if (strp[-1] == '\n')
X strp[-1] = '\0';
X strp = buf;
X dev = atol (strp);
X while (isdigit (*strp))
X strp++;
X ino = atol (strp);
X while (isspace (*strp))
X strp++;
X while (isdigit (*strp))
X strp++;
X strp++;
X add_dir (un_quote_string (strp), dev, ino, (char *) 0);
X }
X fclose (fp);
X}
X
Xvoid
Xwrite_dir_file ()
X{
X FILE *fp;
X struct dirname *dp;
X char *str;
X extern char *quote_copy_string ();
X
X fp = fopen (gnu_dumpfile, "w");
X if (fp == 0)
X {
X msg_perror ("Can't write to %s", gnu_dumpfile);
X return;
X }
X fprintf (fp, "%lu\n", this_time);
X for (dp = dir_list; dp; dp = dp->next)
X {
X if (!dp->dir_text)
X continue;
X str = quote_copy_string (dp->name);
X if (str)
X {
X fprintf (fp, "%u %u %s\n", dp->dev, dp->ino, str);
X free (str);
X }
X else
X fprintf (fp, "%u %u %s\n", dp->dev, dp->ino, dp->name);
X }
X fclose (fp);
X}
X
Xstruct dirname *
Xget_dir (name)
X char *name;
X{
X struct dirname *dp;
X
X for (dp = dir_list; dp; dp = dp->next)
X {
X if (!strcmp (dp->name, name))
X return dp;
X }
X return 0;
X}
X
X
X/* Collect all the names from argv[] (or whatever), then expand them into
X a directory tree, and put all the directories at the beginning. */
Xvoid
Xcollect_and_sort_names ()
X{
X struct name *n, *n_next;
X int num_names;
X struct stat statbuf;
X int name_cmp ();
X char *merge_sort ();
X
X name_gather ();
X
X if (gnu_dumpfile)
X read_dir_file ();
X if (!namelist)
X addname (".");
X for (n = namelist; n; n = n_next)
X {
X n_next = n->next;
X if (n->found || n->dir_contents)
X continue;
X if (n->regexp) /* FIXME just skip regexps for now */
X continue;
X if (n->change_dir)
X if (chdir (n->change_dir) < 0)
X {
X msg_perror ("can't chdir to %s", n->change_dir);
X continue;
X }
X
X#ifdef AIX
X if (statx (n->name, &statbuf, STATSIZE, STX_HIDDEN | STX_LINK))
X#else
X if (lstat (n->name, &statbuf) < 0)
X#endif /* AIX */
X {
X msg_perror ("can't stat %s", n->name);
X continue;
X }
X if (S_ISDIR (statbuf.st_mode))
X {
X n->found++;
X add_dir_name (n->name, statbuf.st_dev);
X }
X }
X
X num_names = 0;
X for (n = namelist; n; n = n->next)
X num_names++;
X namelist = (struct name *) merge_sort ((PTR) namelist, num_names, (char *) (&(namelist->next)) - (char *) namelist, name_cmp);
X
X for (n = namelist; n; n = n->next)
X {
X n->found = 0;
X }
X if (gnu_dumpfile)
X write_dir_file ();
X}
X
Xint
Xname_cmp (n1, n2)
X struct name *n1, *n2;
X{
X if (n1->found)
X {
X if (n2->found)
X return strcmp (n1->name, n2->name);
X else
X return -1;
X }
X else if (n2->found)
X return 1;
X else
X return strcmp (n1->name, n2->name);
X}
X
Xint
Xdirent_cmp (p1, p2)
X const PTR p1;
X const PTR p2;
X{
X char *frst, *scnd;
X
X frst = (*(char **) p1) + 1;
X scnd = (*(char **) p2) + 1;
X
X return strcmp (frst, scnd);
X}
X
Xchar *
Xget_dir_contents (p, device)
X char *p;
X int device;
X{
X DIR *dirp;
X register struct dirent *d;
X char *new_buf;
X char *namebuf;
X int bufsiz;
X int len;
X PTR the_buffer;
X char *buf;
X size_t n_strs;
X /* int n_size;*/
X char *p_buf;
X char **vec, **p_vec;
X
X extern int errno;
X
X errno = 0;
X dirp = opendir (p);
X bufsiz = strlen (p) + NAMSIZ;
X namebuf = ck_malloc (bufsiz + 2);
X if (!dirp)
X {
X if (errno)
X msg_perror ("can't open directory %s", p);
X else
X msg ("error opening directory %s", p);
X new_buf = NULL;
X }
X else
X {
X struct dirname *dp;
X int all_children;
X
X dp = get_dir (p);
X all_children = dp ? dp->allnew : 0;
X (void) strcpy (namebuf, p);
X if (p[strlen (p) - 1] != '/')
X (void) strcat (namebuf, "/");
X len = strlen (namebuf);
X
X the_buffer = init_buffer ();
X while (d = readdir (dirp))
X {
X struct stat hs;
X
X /* Skip . and .. */
X if (is_dot_or_dotdot (d->d_name))
X continue;
X if (NLENGTH (d) + len >= bufsiz)
X {
X bufsiz += NAMSIZ;
X namebuf = ck_realloc (namebuf, bufsiz + 2);
X }
X (void) strcpy (namebuf + len, d->d_name);
X#ifdef AIX
X if (0 != f_follow_links ?
X statx (namebuf, &hs, STATSIZE, STX_HIDDEN) :
X statx (namebuf, &hs, STATSIZE, STX_HIDDEN | STX_LINK))
X#else
X if (0 != f_follow_links ? stat (namebuf, &hs) : lstat (namebuf, &hs))
X#endif
X {
X msg_perror ("can't stat %s", namebuf);
X continue;
X }
X if ((f_local_filesys && device != hs.st_dev)
X || (f_exclude && check_exclude (namebuf)))
X add_buffer (the_buffer, "N", 1);
X#ifdef AIX
X else if (S_ISHIDDEN (hs.st_mode))
X {
X add_buffer (the_buffer, "D", 1);
X strcat (d->d_name, "A");
X d->d_namlen++;
X }
X#endif /* AIX */
X else if (S_ISDIR (hs.st_mode))
X {
X if (dp = get_dir (namebuf))
X {
X if (dp->dev != hs.st_dev
X || dp->ino != hs.st_ino)
X {
X if (f_verbose)
X msg ("directory %s has been renamed.", namebuf);
X dp->allnew = 1;
X dp->dev = hs.st_dev;
X dp->ino = hs.st_ino;
X }
X dp->dir_text = "";
X }
X else
X {
X if (f_verbose)
X msg ("Directory %s is new", namebuf);
X add_dir (namebuf, hs.st_dev, hs.st_ino, "");
X dp = get_dir (namebuf);
X dp->allnew = 1;
X }
X if (all_children)
X dp->allnew = 1;
X
X add_buffer (the_buffer, "D", 1);
X }
X else if (!all_children
X && f_new_files
X && new_time > hs.st_mtime
X && (f_new_files > 1
X || new_time > hs.st_ctime))
X add_buffer (the_buffer, "N", 1);
X else
X add_buffer (the_buffer, "Y", 1);
X add_buffer (the_buffer, d->d_name, (int) (NLENGTH (d) + 1));
X }
X add_buffer (the_buffer, "\000\000", 2);
X closedir (dirp);
X
X /* Well, we've read in the contents of the dir, now sort them */
X buf = get_buffer (the_buffer);
X if (buf[0] == '\0')
X {
X flush_buffer (the_buffer);
X new_buf = NULL;
X }
X else
X {
X n_strs = 0;
X for (p_buf = buf; *p_buf;)
X {
X int tmp;
X
X tmp = strlen (p_buf) + 1;
X n_strs++;
X p_buf += tmp;
X }
X vec = (char **) ck_malloc (sizeof (char *) * (n_strs + 1));
X for (p_vec = vec, p_buf = buf; *p_buf; p_buf += strlen (p_buf) + 1)
X *p_vec++ = p_buf;
X *p_vec = 0;
X qsort ((PTR) vec, n_strs, sizeof (char *), dirent_cmp);
X new_buf = (char *) ck_malloc (p_buf - buf + 2);
X for (p_vec = vec, p_buf = new_buf; *p_vec; p_vec++)
X {
X char *p_tmp;
X
X for (p_tmp = *p_vec; *p_buf++ = *p_tmp++;)
X ;
X }
X *p_buf++ = '\0';
X free (vec);
X flush_buffer (the_buffer);
X }
X }
X free (namebuf);
X return new_buf;
X}
X
X/* p is a directory. Add all the files in P to the namelist. If any of the
X files is a directory, recurse on the subdirectory. . . */
Xstatic void
Xadd_dir_name (p, device)
X char *p;
X int device;
X{
X char *new_buf;
X char *p_buf;
X
X char *namebuf;
X int buflen;
X register int len;
X int sublen;
X
X /* PTR the_buffer;*/
X
X /* char *buf;*/
X /* char **vec,**p_vec;*/
X /* int n_strs,n_size;*/
X
X struct name *n;
X
X int dirent_cmp ();
X
X new_buf = get_dir_contents (p, device);
X
X for (n = namelist; n; n = n->next)
X {
X if (!strcmp (n->name, p))
X {
X n->dir_contents = new_buf ? new_buf : "\0\0\0\0";
X break;
X }
X }
X
X if (new_buf)
X {
X len = strlen (p);
X buflen = NAMSIZ <= len ? len + NAMSIZ : NAMSIZ;
X namebuf = ck_malloc (buflen + 1);
X
X (void) strcpy (namebuf, p);
X if (namebuf[len - 1] != '/')
X {
X namebuf[len++] = '/';
X namebuf[len] = '\0';
X }
X for (p_buf = new_buf; *p_buf; p_buf += sublen + 1)
X {
X sublen = strlen (p_buf);
X if (*p_buf == 'D')
X {
X if (len + sublen >= buflen)
X {
X buflen += NAMSIZ;
X namebuf = ck_realloc (namebuf, buflen + 1);
X }
X (void) strcpy (namebuf + len, p_buf + 1);
X addname (namebuf);
X add_dir_name (namebuf, device);
X }
X }
X free (namebuf);
X }
X}
X
X/* Returns non-zero if p is . or .. This could be a macro for speed. */
Xint
Xis_dot_or_dotdot (p)
X char *p;
X{
X return (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')));
X}
X
X
X
X
X
X
Xvoid
Xgnu_restore (skipcrud)
X int skipcrud;
X{
X char *current_dir;
X /* int current_dir_length; */
X
X char *archive_dir;
X /* int archive_dir_length; */
X PTR the_buffer;
X char *p;
X DIR *dirp;
X struct dirent *d;
X char *cur, *arc;
X extern struct stat hstat; /* Stat struct corresponding */
X long size, copied;
X char *from, *to;
X extern union record *head;
X
X dirp = opendir (skipcrud + current_file_name);
X
X if (!dirp)
X {
X /* The directory doesn't exist now. It'll be created.
X In any case, we don't have to delete any files out
X of it */
X skip_file ((long) hstat.st_size);
X return;
X }
X
X the_buffer = init_buffer ();
X while (d = readdir (dirp))
X {
X if (is_dot_or_dotdot (d->d_name))
X continue;
X
X add_buffer (the_buffer, d->d_name, (int) (NLENGTH (d) + 1));
X }
X closedir (dirp);
X add_buffer (the_buffer, "", 1);
X
X current_dir = get_buffer (the_buffer);
X archive_dir = (char *) ck_malloc (hstat.st_size);
X if (archive_dir == 0)
X {
X msg ("Can't allocate %d bytes for restore", hstat.st_size);
X skip_file ((long) hstat.st_size);
X return;
X }
X to = archive_dir;
X for (size = hstat.st_size; size > 0; size -= copied)
X {
X from = findrec ()->charptr;
X if (!from)
X {
X msg ("Unexpected EOF in archive\n");
X break;
X }
X copied = endofrecs ()->charptr - from;
X if (copied > size)
X copied = size;
X bcopy ((PTR) from, (PTR) to, (int) copied);
X to += copied;
X userec ((union record *) (from + copied - 1));
X }
X
X for (cur = current_dir; *cur; cur += strlen (cur) + 1)
X {
X for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
X {
X arc++;
X if (!strcmp (arc, cur))
X break;
X }
X if (*arc == '\0')
X {
X p = new_name (skipcrud + current_file_name, cur);
X if (f_confirm && !confirm ("delete", p))
X {
X free (p);
X continue;
X }
X if (f_verbose)
X fprintf (msg_file, "%s: deleting %s\n", tar, p);
X if (recursively_delete (p))
X {
X msg ("%s: Error while deleting %s\n", tar, p);
X }
X free (p);
X }
X
X }
X flush_buffer (the_buffer);
X free (archive_dir);
X}
X
Xint
Xrecursively_delete (path)
X char *path;
X{
X struct stat sbuf;
X DIR *dirp;
X struct dirent *dp;
X char *path_buf;
X /* int path_len; */
X
X
X if (lstat (path, &sbuf) < 0)
X return 1;
X if (S_ISDIR (sbuf.st_mode))
X {
X
X /* path_len=strlen(path); */
X dirp = opendir (path);
X if (dirp == 0)
X return 1;
X while (dp = readdir (dirp))
X {
X if (is_dot_or_dotdot (dp->d_name))
X continue;
X path_buf = new_name (path, dp->d_name);
X if (recursively_delete (path_buf))
X {
X free (path_buf);
X closedir (dirp);
X return 1;
X }
X free (path_buf);
X }
X closedir (dirp);
X
X if (rmdir (path) < 0)
X return 1;
X return 0;
X }
X if (unlink (path) < 0)
X return 1;
X return 0;
X}
END_OF_FILE
if test 13964 -ne `wc -c <'gnu.c'`; then
echo shar: \"'gnu.c'\" unpacked with wrong size!
fi
# end of 'gnu.c'
fi
if test -f 'mangle.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'mangle.c'\"
else
echo shar: Extracting \"'mangle.c'\" \(6635 characters\)
sed "s/^X//" >'mangle.c' <<'END_OF_FILE'
X/* mangle.c -- encode long filenames
X Copyright (C) 1988, 1992 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <time.h>
Xtime_t time ();
X
X#include "tar.h"
X#include "port.h"
X
Xvoid add_buffer ();
Xextern PTR ck_malloc ();
Xvoid finish_header ();
Xextern PTR init_buffer ();
Xextern char *quote_copy_string ();
Xextern char *get_buffer ();
Xchar *un_quote_string ();
X
Xextern union record *start_header ();
X
Xextern struct stat hstat; /* Stat struct corresponding */
X
Xstruct mangled
X {
X struct mangled *next;
X int type;
X char mangled[NAMSIZ];
X char *linked_to;
X char normal[1];
X };
X
X
X/* Should use a hash table, etc. . */
Xstruct mangled *first_mangle;
Xint mangled_num = 0;
X
X#if 0 /* Deleted because there is now a better way to do all this */
X
Xchar *
Xfind_mangled (name)
X char *name;
X{
X struct mangled *munge;
X
X for (munge = first_mangle; munge; munge = munge->next)
X if (!strcmp (name, munge->normal))
X return munge->mangled;
X return 0;
X}
X
X
X#ifdef S_ISLNK
Xvoid
Xadd_symlink_mangle (symlink, linkto, buffer)
X char *symlink;
X char *linkto;
X char *buffer;
X{
X struct mangled *munge, *kludge;
X
X munge = (struct mangled *) ck_malloc (sizeof (struct mangled) + strlen (symlink) + strlen (linkto) + 2);
X if (!first_mangle)
X first_mangle = munge;
X else
X {
X for (kludge = first_mangle; kludge->next; kludge = kludge->next)
X ;
X kludge->next = munge;
X }
X munge->type = 1;
X munge->next = 0;
X strcpy (munge->normal, symlink);
X munge->linked_to = munge->normal + strlen (symlink) + 1;
X strcpy (munge->linked_to, linkto);
X sprintf (munge->mangled, "@@MaNgLeD.%d", mangled_num++);
X strncpy (buffer, munge->mangled, NAMSIZ);
X}
X
X#endif
X
Xvoid
Xadd_mangle (name, buffer)
X char *name;
X char *buffer;
X{
X struct mangled *munge, *kludge;
X
X munge = (struct mangled *) ck_malloc (sizeof (struct mangled) + strlen (name));
X if (!first_mangle)
X first_mangle = munge;
X else
X {
X for (kludge = first_mangle; kludge->next; kludge = kludge->next)
X ;
X kludge->next = munge;
X }
X munge->next = 0;
X munge->type = 0;
X strcpy (munge->normal, name);
X sprintf (munge->mangled, "@@MaNgLeD.%d", mangled_num++);
X strncpy (buffer, munge->mangled, NAMSIZ);
X}
X
Xvoid
Xwrite_mangled ()
X{
X struct mangled *munge;
X struct stat hstat;
X union record *header;
X char *ptr1, *ptr2;
X PTR the_buffer;
X int size;
X int bufsize;
X
X if (!first_mangle)
X return;
X the_buffer = init_buffer ();
X for (munge = first_mangle, size = 0; munge; munge = munge->next)
X {
X ptr1 = quote_copy_string (munge->normal);
X if (!ptr1)
X ptr1 = munge->normal;
X if (munge->type)
X {
X add_buffer (the_buffer, "Symlink ", 8);
X add_buffer (the_buffer, ptr1, strlen (ptr1));
X add_buffer (the_buffer, " to ", 4);
X
X if (ptr2 = quote_copy_string (munge->linked_to))
X {
X add_buffer (the_buffer, ptr2, strlen (ptr2));
X free (ptr2);
X }
X else
X add_buffer (the_buffer, munge->linked_to, strlen (munge->linked_to));
X }
X else
X {
X add_buffer (the_buffer, "Rename ", 7);
X add_buffer (the_buffer, munge->mangled, strlen (munge->mangled));
X add_buffer (the_buffer, " to ", 4);
X add_buffer (the_buffer, ptr1, strlen (ptr1));
X }
X add_buffer (the_buffer, "\n", 1);
X if (ptr1 != munge->normal)
X free (ptr1);
X }
X
X bzero (&hstat, sizeof (struct stat));
X hstat.st_atime = hstat.st_mtime = hstat.st_ctime = time (0);
X ptr1 = get_buffer (the_buffer);
X hstat.st_size = strlen (ptr1);
X
X header = start_header ("././@MaNgLeD_NaMeS", &hstat);
X header->header.linkflag = LF_NAMES;
X finish_header (header);
X size = hstat.st_size;
X header = findrec ();
X bufsize = endofrecs ()->charptr - header->charptr;
X
X while (bufsize < size)
X {
X bcopy (ptr1, header->charptr, bufsize);
X ptr1 += bufsize;
X size -= bufsize;
X userec (header + (bufsize - 1) / RECORDSIZE);
X header = findrec ();
X bufsize = endofrecs ()->charptr - header->charptr;
X }
X bcopy (ptr1, header->charptr, size);
X bzero (header->charptr + size, bufsize - size);
X userec (header + (size - 1) / RECORDSIZE);
X}
X
X#endif
X
Xvoid
Xextract_mangle (head)
X union record *head;
X{
X char *buf;
X char *fromtape;
X char *to;
X char *ptr, *ptrend;
X char *nam1, *nam1end;
X int size;
X int copied;
X
X size = hstat.st_size;
X buf = to = ck_malloc (size + 1);
X buf[size] = '\0';
X while (size > 0)
X {
X fromtape = findrec ()->charptr;
X if (fromtape == 0)
X {
X msg ("Unexpected EOF in mangled names!");
X return;
X }
X copied = endofrecs ()->charptr - fromtape;
X if (copied > size)
X copied = size;
X bcopy (fromtape, to, copied);
X to += copied;
X size -= copied;
X userec ((union record *) (fromtape + copied - 1));
X }
X for (ptr = buf; *ptr; ptr = ptrend)
X {
X ptrend = index (ptr, '\n');
X *ptrend++ = '\0';
X
X if (!strncmp (ptr, "Rename ", 7))
X {
X nam1 = ptr + 7;
X nam1end = index (nam1, ' ');
X while (strncmp (nam1end, " to ", 4))
X {
X nam1end++;
X nam1end = index (nam1end, ' ');
X }
X *nam1end = '\0';
X if (ptrend[-2] == '/')
X ptrend[-2] = '\0';
X un_quote_string (nam1end + 4);
X if (rename (nam1, nam1end + 4))
X msg_perror ("Can't rename %s to %s", nam1, nam1end + 4);
X else if (f_verbose)
X msg ("Renamed %s to %s", nam1, nam1end + 4);
X }
X#ifdef S_ISLNK
X else if (!strncmp (ptr, "Symlink ", 8))
X {
X nam1 = ptr + 8;
X nam1end = index (nam1, ' ');
X while (strncmp (nam1end, " to ", 4))
X {
X nam1end++;
X nam1end = index (nam1end, ' ');
X }
X *nam1end = '\0';
X un_quote_string (nam1);
X un_quote_string (nam1end + 4);
X if (symlink (nam1, nam1end + 4) && (unlink (nam1end + 4) || symlink (nam1, nam1end + 4)))
X msg_perror ("Can't symlink %s to %s", nam1, nam1end + 4);
X else if (f_verbose)
X msg ("Symlinkd %s to %s", nam1, nam1end + 4);
X }
X#endif
X else
X msg ("Unknown demangling command %s", ptr);
X }
X}
END_OF_FILE
if test 6635 -ne `wc -c <'mangle.c'`; then
echo shar: \"'mangle.c'\" unpacked with wrong size!
fi
# end of 'mangle.c'
fi
if test -f 'version.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'version.c'\"
else
echo shar: Extracting \"'version.c'\" \(50 characters\)
sed "s/^X//" >'version.c' <<'END_OF_FILE'
Xchar version_string[] = "GNU tar version 1.11.2";
END_OF_FILE
if test 50 -ne `wc -c <'version.c'`; then
echo shar: \"'version.c'\" unpacked with wrong size!
fi
# end of 'version.c'
fi
if test -f 'list.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'list.c'\"
else
echo shar: Extracting \"'list.c'\" \(20047 characters\)
sed "s/^X//" >'list.c' <<'END_OF_FILE'
X/* List a tar archive.
X Copyright (C) 1988, 1992, 1993 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/*
X * List a tar archive.
X *
X * Also includes support routines for reading a tar archive.
X *
X * this version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu).
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include <errno.h>
X#ifndef STDC_HEADERS
Xextern int errno;
X#endif
X#include <time.h>
X
X#ifdef BSD42
X#include <sys/file.h>
X#else
X#ifndef V7
X#include <fcntl.h>
X#endif
X#endif
X
X#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
X
X#include "tar.h"
X#include "port.h"
X
Xextern FILE *msg_file;
X
Xlong from_oct (); /* Decode octal number */
Xvoid demode (); /* Print file mode */
Xvoid restore_saved_dir_info ();
XPTR ck_malloc ();
X
Xunion record *head; /* Points to current archive header */
Xstruct stat hstat; /* Stat struct corresponding */
Xint head_standard; /* Tape header is in ANSI format */
X
Xint check_exclude ();
Xvoid close_archive ();
Xvoid decode_header ();
Xint findgid ();
Xint finduid ();
Xvoid name_gather ();
Xint name_match ();
Xvoid names_notfound ();
Xvoid open_archive ();
Xvoid print_header ();
Xint read_header ();
Xvoid saverec ();
Xvoid skip_file ();
Xvoid skip_extended_headers ();
X
Xextern char *quote_copy_string ();
X
X
X/*
X * Main loop for reading an archive.
X */
Xvoid
Xread_and (do_something)
X void (*do_something) ();
X{
X int status = 3; /* Initial status at start of archive */
X int prev_status;
X extern time_t new_time;
X char save_linkflag;
X
X name_gather (); /* Gather all the names */
X open_archive (1); /* Open for reading */
X
X for (;;)
X {
X prev_status = status;
X status = read_header ();
X switch (status)
X {
X
X case 1: /* Valid header */
X /* We should decode next field (mode) first... */
X /* Ensure incoming names are null terminated. */
X
X if (!name_match (current_file_name)
X || (f_new_files && hstat.st_mtime < new_time)
X || (f_exclude && check_exclude (current_file_name)))
X {
X
X int isextended = 0;
X
X if (head->header.linkflag == LF_VOLHDR
X || head->header.linkflag == LF_MULTIVOL
X || head->header.linkflag == LF_NAMES)
X {
X (*do_something) ();
X continue;
X }
X if (f_show_omitted_dirs
X && head->header.linkflag == LF_DIR)
X msg ("Omitting %s\n", current_file_name);
X /* Skip past it in the archive */
X if (head->header.isextended)
X isextended = 1;
X save_linkflag = head->header.linkflag;
X userec (head);
X if (isextended)
X {
X /* register union record *exhdr;
X
X for (;;) {
X exhdr = findrec();
X if (!exhdr->ext_hdr.isextended) {
X userec(exhdr);
X break;
X }
X }
X userec(exhdr);*/
X skip_extended_headers ();
X }
X /* Skip to the next header on the archive */
X if (save_linkflag != LF_DIR)
X skip_file ((long) hstat.st_size);
X continue;
X
X }
X
X (*do_something) ();
X continue;
X
X /*
X * If the previous header was good, tell them
X * that we are skipping bad ones.
X */
X case 0: /* Invalid header */
X userec (head);
X switch (prev_status)
X {
X case 3: /* Error on first record */
X msg ("Hmm, this doesn't look like a tar archive.");
X /* FALL THRU */
X case 2: /* Error after record of zeroes */
X case 1: /* Error after header rec */
X msg ("Skipping to next file header...");
X case 0: /* Error after error */
X break;
X }
X continue;
X
X case 2: /* Record of zeroes */
X userec (head);
X status = prev_status; /* If error after 0's */
X if (f_ignorez)
X continue;
X /* FALL THRU */
X case EOF: /* End of archive */
X break;
X }
X break;
X };
X
X restore_saved_dir_info ();
X close_archive ();
X names_notfound (); /* Print names not found */
X}
X
X
X/*
X * Print a header record, based on tar options.
X */
Xvoid
Xlist_archive ()
X{
X extern char *save_name;
X int isextended = 0; /* Flag to remember if head is extended */
X
X /* Save the record */
X saverec (&head);
X
X /* Print the header record */
X if (f_verbose)
X {
X if (f_verbose > 1)
X decode_header (head, &hstat, &head_standard, 0);
X print_header ();
X }
X
X if (f_gnudump && head->header.linkflag == LF_DUMPDIR)
X {
X size_t size, written, check;
X char *data;
X extern long save_totsize;
X extern long save_sizeleft;
X
X userec (head);
X if (f_multivol)
X {
X save_name = current_file_name;
X save_totsize = hstat.st_size;
X }
X for (size = hstat.st_size; size > 0; size -= written)
X {
X if (f_multivol)
X save_sizeleft = size;
X data = findrec ()->charptr;
X if (data == NULL)
X {
X msg ("EOF in archive file?");
X break;
X }
X written = endofrecs ()->charptr - data;
X if (written > size)
X written = size;
X errno = 0;
X check = fwrite (data, sizeof (char), written, msg_file);
X userec ((union record *) (data + written - 1));
X if (check != written)
X {
X msg_perror ("only wrote %ld of %ld bytes to file %s", check, written, current_file_name);
X skip_file ((long) (size) - written);
X break;
X }
X }
X if (f_multivol)
X save_name = 0;
X saverec ((union record **) 0); /* Unsave it */
X fputc ('\n', msg_file);
X fflush (msg_file);
X return;
X
X }
X saverec ((union record **) 0);/* Unsave it */
X /* Check to see if we have an extended header to skip over also */
X if (head->header.isextended)
X isextended = 1;
X
X /* Skip past the header in the archive */
X userec (head);
X
X /*
X * If we needed to skip any extended headers, do so now, by
X * reading extended headers and skipping past them in the
X * archive.
X */
X if (isextended)
X {
X /* register union record *exhdr;
X
X for (;;) {
X exhdr = findrec();
X
X if (!exhdr->ext_hdr.isextended) {
X userec(exhdr);
X break;
X }
X userec(exhdr);
X }*/
X skip_extended_headers ();
X }
X
X if (f_multivol)
X save_name = current_file_name;
X /* Skip to the next header on the archive */
X
X skip_file ((long) hstat.st_size);
X
X if (f_multivol)
X save_name = 0;
X}
X
X
X/*
X * Read a record that's supposed to be a header record.
X * Return its address in "head", and if it is good, the file's
X * size in hstat.st_size.
X *
X * Return 1 for success, 0 if the checksum is bad, EOF on eof,
X * 2 for a record full of zeros (EOF marker).
X *
X * You must always userec(head) to skip past the header which this
X * routine reads.
X */
Xint
Xread_header ()
X{
X register int i;
X register long sum, signed_sum, recsum;
X register char *p;
X register union record *header;
X long from_oct ();
X char **longp;
X char *bp, *data;
X int size, written;
X static char *next_long_name, *next_long_link;
X char *name;
X
Xrecurse:
X
X header = findrec ();
X head = header; /* This is our current header */
X if (NULL == header)
X return EOF;
X
X recsum = from_oct (8, header->header.chksum);
X
X sum = 0;
X p = header->charptr;
X for (i = sizeof (*header); --i >= 0;)
X {
X /*
X * We can't use unsigned char here because of old compilers,
X * e.g. V7.
X */
X signed_sum += *p;
X sum += 0xFF & *p++;
X }
X
X /* Adjust checksum to count the "chksum" field as blanks. */
X for (i = sizeof (header->header.chksum); --i >= 0;)
X {
X sum -= 0xFF & header->header.chksum[i];
X signed_sum -= (char) header->header.chksum[i];
X }
X sum += ' ' * sizeof header->header.chksum;
X signed_sum += ' ' * sizeof header->header.chksum;
X
X if (sum == 8 * ' ')
X {
X /*
X * This is a zeroed record...whole record is 0's except
X * for the 8 blanks we faked for the checksum field.
X */
X return 2;
X }
X
X if (sum != recsum && signed_sum != recsum)
X return 0;
X
X /*
X * Good record. Decode file size and return.
X */
X if (header->header.linkflag == LF_LINK)
X hstat.st_size = 0; /* Links 0 size on tape */
X else
X hstat.st_size = from_oct (1 + 12, header->header.size);
X
X header->header.arch_name[NAMSIZ - 1] = '\0';
X if (header->header.linkflag == LF_LONGNAME
X || header->header.linkflag == LF_LONGLINK)
X {
X longp = ((header->header.linkflag == LF_LONGNAME)
X ? &next_long_name
X : &next_long_link);
X
X userec (header);
X if (*longp)
X free (*longp);
X bp = *longp = (char *) ck_malloc (hstat.st_size);
X
X for (size = hstat.st_size;
X size > 0;
X size -= written)
X {
X data = findrec ()->charptr;
X if (data == NULL)
X {
X msg ("Unexpected EOF on archive file");
X break;
X }
X written = endofrecs ()->charptr - data;
X if (written > size)
X written = size;
X
X bcopy (data, bp, written);
X bp += written;
X userec ((union record *) (data + written - 1));
X }
X goto recurse;
X }
X else
X {
X name = (next_long_name
X ? next_long_name
X : head->header.arch_name);
X if (current_file_name)
X free (current_file_name);
X current_file_name = ck_malloc (strlen (name) + 1);
X strcpy (current_file_name, name);
X
X name = (next_long_link
X ? next_long_link
X : head->header.arch_linkname);
X if (current_link_name)
X free (current_link_name);
X current_link_name = ck_malloc (strlen (name) + 1);
X strcpy (current_link_name, name);
X
X next_long_link = next_long_name = 0;
X return 1;
X }
X}
X
X
X/*
X * Decode things from a file header record into a "struct stat".
X * Also set "*stdp" to !=0 or ==0 depending whether header record is "Unix
X * Standard" tar format or regular old tar format.
X *
X * read_header() has already decoded the checksum and length, so we don't.
X *
X * If wantug != 0, we want the uid/group info decoded from Unix Standard
X * tapes (for extraction). If == 0, we are just printing anyway, so save time.
X *
X * decode_header should NOT be called twice for the same record, since the
X * two calls might use different "wantug" values and thus might end up with
X * different uid/gid for the two calls. If anybody wants the uid/gid they
X * should decode it first, and other callers should decode it without uid/gid
X * before calling a routine, e.g. print_header, that assumes decoded data.
X */
Xvoid
Xdecode_header (header, st, stdp, wantug)
X register union record *header;
X register struct stat *st;
X int *stdp;
X int wantug;
X{
X long from_oct ();
X
X st->st_mode = from_oct (8, header->header.mode);
X st->st_mode &= 07777;
X st->st_mtime = from_oct (1 + 12, header->header.mtime);
X if (f_gnudump)
X {
X st->st_atime = from_oct (1 + 12, header->header.atime);
X st->st_ctime = from_oct (1 + 12, header->header.ctime);
X }
X
X if (0 == strcmp (header->header.magic, TMAGIC))
X {
X /* Unix Standard tar archive */
X *stdp = 1;
X if (wantug)
X {
X#ifdef NONAMES
X st->st_uid = from_oct (8, header->header.uid);
X st->st_gid = from_oct (8, header->header.gid);
X#else
X st->st_uid =
X (*header->header.uname
X ? finduid (header->header.uname)
X : from_oct (8, header->header.uid));
X st->st_gid =
X (*header->header.gname
X ? findgid (header->header.gname)
X : from_oct (8, header->header.gid));
X#endif
X }
X#if defined(S_IFBLK) || defined(S_IFCHR)
X switch (header->header.linkflag)
X {
X case LF_BLK:
X case LF_CHR:
X st->st_rdev = makedev (from_oct (8, header->header.devmajor),
X from_oct (8, header->header.devminor));
X }
X#endif
X }
X else
X {
X /* Old fashioned tar archive */
X *stdp = 0;
X st->st_uid = from_oct (8, header->header.uid);
X st->st_gid = from_oct (8, header->header.gid);
X st->st_rdev = 0;
X }
X}
X
X
X/*
X * Quick and dirty octal conversion.
X *
X * Result is -1 if the field is invalid (all blank, or nonoctal).
X */
Xlong
Xfrom_oct (digs, where)
X register int digs;
X register char *where;
X{
X register long value;
X
X while (isspace (*where))
X { /* Skip spaces */
X where++;
X if (--digs <= 0)
X return -1; /* All blank field */
X }
X value = 0;
X while (digs > 0 && isodigit (*where))
X { /* Scan til nonoctal */
X value = (value << 3) | (*where++ - '0');
X --digs;
X }
X
X if (digs > 0 && *where && !isspace (*where))
X return -1; /* Ended on non-space/nul */
X
X return value;
X}
X
X
X/*
X * Actually print it.
X *
X * Plain and fancy file header block logging.
X * Non-verbose just prints the name, e.g. for "tar t" or "tar x".
X * This should just contain file names, so it can be fed back into tar
X * with xargs or the "-T" option. The verbose option can give a bunch
X * of info, one line per file. I doubt anybody tries to parse its
X * format, or if they do, they shouldn't. Unix tar is pretty random here
X * anyway.
X *
X * Note that print_header uses the globals <head>, <hstat>, and
X * <head_standard>, which must be set up in advance. This is not very clean
X * and should be cleaned up. FIXME.
X */
X#define UGSWIDTH 18 /* min width of User, group, size */
X/* UGSWIDTH of 18 means that with user and group names <= 8 chars the columns
X never shift during the listing. */
X#define DATEWIDTH 19 /* Last mod date */
Xstatic int ugswidth = UGSWIDTH; /* Max width encountered so far */
X
Xvoid
Xprint_header ()
X{
X char modes[11];
X char *timestamp;
X char uform[11], gform[11]; /* These hold formatted ints */
X char *user, *group;
X char size[24]; /* Holds a formatted long or maj, min */
X time_t longie; /* To make ctime() call portable */
X int pad;
X char *name;
X extern long baserec;
X
X if (f_sayblock)
X fprintf (msg_file, "rec %10d: ", baserec + (ar_record - ar_block));
X /* annofile(msg_file, (char *)NULL); */
X
X if (f_verbose <= 1)
X {
X /* Just the fax, mam. */
X char *name;
X
X name = quote_copy_string (current_file_name);
X if (name == 0)
X name = current_file_name;
X fprintf (msg_file, "%s\n", name);
X if (name != current_file_name)
X free (name);
X }
X else
X {
X /* File type and modes */
X modes[0] = '?';
X switch (head->header.linkflag)
X {
X case LF_VOLHDR:
X modes[0] = 'V';
X break;
X
X case LF_MULTIVOL:
X modes[0] = 'M';
X break;
X
X case LF_NAMES:
X modes[0] = 'N';
X break;
X
X case LF_LONGNAME:
X case LF_LONGLINK:
X msg ("Visible longname error\n");
X break;
X
X case LF_SPARSE:
X case LF_NORMAL:
X case LF_OLDNORMAL:
X case LF_LINK:
X modes[0] = '-';
X if ('/' == current_file_name[strlen (current_file_name) - 1])
X modes[0] = 'd';
X break;
X case LF_DUMPDIR:
X modes[0] = 'd';
X break;
X case LF_DIR:
X modes[0] = 'd';
X break;
X case LF_SYMLINK:
X modes[0] = 'l';
X break;
X case LF_BLK:
X modes[0] = 'b';
X break;
X case LF_CHR:
X modes[0] = 'c';
X break;
X case LF_FIFO:
X modes[0] = 'p';
X break;
X case LF_CONTIG:
X modes[0] = 'C';
X break;
X }
X
X demode ((unsigned) hstat.st_mode, modes + 1);
X
X /* Timestamp */
X longie = hstat.st_mtime;
X timestamp = ctime (&longie);
X timestamp[16] = '\0';
X timestamp[24] = '\0';
X
X /* User and group names */
X if (*head->header.uname && head_standard)
X {
X user = head->header.uname;
X }
X else
X {
X user = uform;
X (void) sprintf (uform, "%d",
X from_oct (8, head->header.uid));
X }
X if (*head->header.gname && head_standard)
X {
X group = head->header.gname;
X }
X else
X {
X group = gform;
X (void) sprintf (gform, "%d",
X from_oct (8, head->header.gid));
X }
X
X /* Format the file size or major/minor device numbers */
X switch (head->header.linkflag)
X {
X#if defined(S_IFBLK) || defined(S_IFCHR)
X case LF_CHR:
X case LF_BLK:
X (void) sprintf (size, "%d,%d",
X major (hstat.st_rdev),
X minor (hstat.st_rdev));
X break;
X#endif
X case LF_SPARSE:
X (void) sprintf (size, "%ld",
X from_oct (1 + 12, head->header.realsize));
X break;
X default:
X (void) sprintf (size, "%ld", (long) hstat.st_size);
X }
X
X /* Figure out padding and print the whole line. */
X pad = strlen (user) + strlen (group) + strlen (size) + 1;
X if (pad > ugswidth)
X ugswidth = pad;
X
X name = quote_copy_string (current_file_name);
X if (!name)
X name = current_file_name;
X fprintf (msg_file, "%s %s/%s %*s%s %s %s %s",
X modes,
X user,
X group,
X ugswidth - pad,
X "",
X size,
X timestamp + 4, timestamp + 20,
X name);
X
X if (name != current_file_name)
X free (name);
X switch (head->header.linkflag)
X {
X case LF_SYMLINK:
X name = quote_copy_string (current_link_name);
X if (!name)
X name = current_link_name;
X fprintf (msg_file, " -> %s\n", name);
X if (name != current_link_name)
X free (name);
X break;
X
X case LF_LINK:
X name = quote_copy_string (current_link_name);
X if (!name)
X name = current_link_name;
X fprintf (msg_file, " link to %s\n", current_link_name);
X if (name != current_link_name)
X free (name);
X break;
X
X default:
X fprintf (msg_file, " unknown file type '%c'\n",
X head->header.linkflag);
X break;
X
X case LF_OLDNORMAL:
X case LF_NORMAL:
X case LF_SPARSE:
X case LF_CHR:
X case LF_BLK:
X case LF_DIR:
X case LF_FIFO:
X case LF_CONTIG:
X case LF_DUMPDIR:
X putc ('\n', msg_file);
X break;
X
X case LF_VOLHDR:
X fprintf (msg_file, "--Volume Header--\n");
X break;
X
X case LF_MULTIVOL:
X fprintf (msg_file, "--Continued at byte %ld--\n", from_oct (1 + 12, head->header.offset));
X break;
X
X case LF_NAMES:
X fprintf (msg_file, "--Mangled file names--\n");
X break;
X }
X }
X fflush (msg_file);
X}
X
X/*
X * Print a similar line when we make a directory automatically.
X */
Xvoid
Xpr_mkdir (pathname, length, mode)
X char *pathname;
X int length;
X int mode;
X{
X char modes[11];
X char *name;
X extern long baserec;
X
X if (f_verbose > 1)
X {
X /* File type and modes */
X modes[0] = 'd';
X demode ((unsigned) mode, modes + 1);
X
X if (f_sayblock)
X fprintf (msg_file, "rec %10d: ", baserec + (ar_record - ar_block));
X /* annofile(msg_file, (char *)NULL); */
X name = quote_copy_string (pathname);
X if (!name)
X name = pathname;
X fprintf (msg_file, "%s %*s %.*s\n",
X modes,
X ugswidth + DATEWIDTH,
X "Creating directory:",
X length,
X pathname);
X if (name != pathname)
X free (name);
X }
X}
X
X
X/*
X * Skip over <size> bytes of data in records in the archive.
X */
Xvoid
Xskip_file (size)
X register long size;
X{
X union record *x;
X extern long save_totsize;
X extern long save_sizeleft;
X
X if (f_multivol)
X {
X save_totsize = size;
X save_sizeleft = size;
X }
X
X while (size > 0)
X {
X x = findrec ();
X if (x == NULL)
X { /* Check it... */
X msg ("Unexpected EOF on archive file");
X exit (EX_BADARCH);
X }
X userec (x);
X size -= RECORDSIZE;
X if (f_multivol)
X save_sizeleft -= RECORDSIZE;
X }
X}
X
Xvoid
Xskip_extended_headers ()
X{
X register union record *exhdr;
X
X for (;;)
X {
X exhdr = findrec ();
X if (!exhdr->ext_hdr.isextended)
X {
X userec (exhdr);
X break;
X }
X userec (exhdr);
X }
X}
X
X/*
X * Decode the mode string from a stat entry into a 9-char string and a null.
X */
Xvoid
Xdemode (mode, string)
X register unsigned mode;
X register char *string;
X{
X register unsigned mask;
X register char *rwx = "rwxrwxrwx";
X
X for (mask = 0400; mask != 0; mask >>= 1)
X {
X if (mode & mask)
X *string++ = *rwx++;
X else
X {
X *string++ = '-';
X rwx++;
X }
X }
X
X if (mode & S_ISUID)
X if (string[-7] == 'x')
X string[-7] = 's';
X else
X string[-7] = 'S';
X if (mode & S_ISGID)
X if (string[-4] == 'x')
X string[-4] = 's';
X else
X string[-4] = 'S';
X if (mode & S_ISVTX)
X if (string[-1] == 'x')
X string[-1] = 't';
X else
X string[-1] = 'T';
X *string = '\0';
X}
END_OF_FILE
if test 20047 -ne `wc -c <'list.c'`; then
echo shar: \"'list.c'\" unpacked with wrong size!
fi
# end of 'list.c'
fi
if test -f 'names.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'names.c'\"
else
echo shar: Extracting \"'names.c'\" \(3302 characters\)
sed "s/^X//" >'names.c' <<'END_OF_FILE'
X/* Look up user and/or group names.
X Copyright (C) 1988, 1992 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/*
X * Look up user and/or group names.
X *
X * This file should be modified for non-unix systems to do something
X * reasonable.
X */
X
X#include <sys/types.h>
X#include "tar.h"
X#include "port.h"
X
X#ifndef NONAMES
X/* Whole module goes away if NONAMES defined. Otherwise... */
X#include <stdio.h>
X#include <pwd.h>
X#include <grp.h>
X
Xstatic int saveuid = -993;
Xstatic char saveuname[TUNMLEN];
Xstatic int my_uid = -993;
X
Xstatic int savegid = -993;
Xstatic char savegname[TGNMLEN];
Xstatic int my_gid = -993;
X
X#define myuid ( my_uid < 0? (my_uid = getuid()): my_uid )
X#define mygid ( my_gid < 0? (my_gid = getgid()): my_gid )
X
X/*
X * Look up a user or group name from a uid/gid, maintaining a cache.
X * FIXME, for now it's a one-entry cache.
X * FIXME2, the "-993" is to reduce the chance of a hit on the first lookup.
X *
X * This is ifdef'd because on Suns, it drags in about 38K of "yellow
X * pages" code, roughly doubling the program size. Thanks guys.
X */
Xvoid
Xfinduname (uname, uid)
X char uname[TUNMLEN];
X int uid;
X{
X struct passwd *pw;
X#ifndef HAVE_GETPWUID
X extern struct passwd *getpwuid ();
X#endif
X
X if (uid != saveuid)
X {
X saveuid = uid;
X saveuname[0] = '\0';
X pw = getpwuid (uid);
X if (pw)
X strncpy (saveuname, pw->pw_name, TUNMLEN);
X }
X strncpy (uname, saveuname, TUNMLEN);
X}
X
Xint
Xfinduid (uname)
X char uname[TUNMLEN];
X{
X struct passwd *pw;
X extern struct passwd *getpwnam ();
X
X if (uname[0] != saveuname[0] /* Quick test w/o proc call */
X || 0 != strncmp (uname, saveuname, TUNMLEN))
X {
X strncpy (saveuname, uname, TUNMLEN);
X pw = getpwnam (uname);
X if (pw)
X {
X saveuid = pw->pw_uid;
X }
X else
X {
X saveuid = myuid;
X }
X }
X return saveuid;
X}
X
X
Xvoid
Xfindgname (gname, gid)
X char gname[TGNMLEN];
X int gid;
X{
X struct group *gr;
X#ifndef HAVE_GETGRGID
X extern struct group *getgrgid ();
X#endif
X
X if (gid != savegid)
X {
X savegid = gid;
X savegname[0] = '\0';
X (void) setgrent ();
X gr = getgrgid (gid);
X if (gr)
X strncpy (savegname, gr->gr_name, TGNMLEN);
X }
X (void) strncpy (gname, savegname, TGNMLEN);
X}
X
X
Xint
Xfindgid (gname)
X char gname[TUNMLEN];
X{
X struct group *gr;
X extern struct group *getgrnam ();
X
X if (gname[0] != savegname[0] /* Quick test w/o proc call */
X || 0 != strncmp (gname, savegname, TUNMLEN))
X {
X strncpy (savegname, gname, TUNMLEN);
X gr = getgrnam (gname);
X if (gr)
X {
X savegid = gr->gr_gid;
X }
X else
X {
X savegid = mygid;
X }
X }
X return savegid;
X}
X
X#endif
END_OF_FILE
if test 3302 -ne `wc -c <'names.c'`; then
echo shar: \"'names.c'\" unpacked with wrong size!
fi
# end of 'names.c'
fi
if test -f 'diffarch.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'diffarch.c'\"
else
echo shar: Extracting \"'diffarch.c'\" \(16692 characters\)
sed "s/^X//" >'diffarch.c' <<'END_OF_FILE'
X/* Diff files from a tar archive.
X Copyright (C) 1988, 1992, 1993 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/*
X * Diff files from a tar archive.
X *
X * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
X */
X
X#include <stdio.h>
X#include <errno.h>
X#ifndef STDC_HEADERS
Xextern int errno;
X#endif
X#include <sys/types.h>
X
X#ifdef BSD42
X#include <sys/file.h>
X#else
X#ifndef V7
X#include <fcntl.h>
X#endif
X#endif
X
X#ifdef HAVE_SYS_MTIO_H
X#include <sys/ioctl.h>
X#include <sys/mtio.h>
X#endif
X
X#include "tar.h"
X#include "port.h"
X#include "rmt.h"
X
X#ifndef S_ISLNK
X#define lstat stat
X#endif
X
Xextern void *valloc ();
X
Xextern union record *head; /* Points to current tape header */
Xextern struct stat hstat; /* Stat struct corresponding */
Xextern int head_standard; /* Tape header is in ANSI format */
X
Xvoid decode_header ();
Xvoid diff_sparse_files ();
Xvoid fill_in_sparse_array ();
Xvoid fl_read ();
Xlong from_oct ();
Xint do_stat ();
Xextern void print_header ();
Xint read_header ();
Xvoid saverec ();
Xvoid sigh ();
Xextern void skip_file ();
Xextern void skip_extended_headers ();
Xint wantbytes ();
X
Xextern FILE *msg_file;
X
Xint now_verifying = 0; /* Are we verifying at the moment? */
X
Xint diff_fd; /* Descriptor of file we're diffing */
X
Xchar *diff_buf = 0; /* Pointer to area for reading
X file contents into */
X
Xchar *diff_dir; /* Directory contents for LF_DUMPDIR */
X
Xint different = 0;
X
X/*struct sp_array *sparsearray;
Xint sp_ar_size = 10;*/
X/*
X * Initialize for a diff operation
X */
Xvoid
Xdiff_init ()
X{
X /*NOSTRICT*/
X diff_buf = (char *) valloc ((unsigned) blocksize);
X if (!diff_buf)
X {
X msg ("could not allocate memory for diff buffer of %d bytes",
X blocksize);
X exit (EX_ARGSBAD);
X }
X}
X
X/*
X * Diff a file against the archive.
X */
Xvoid
Xdiff_archive ()
X{
X register char *data;
X int check, namelen;
X int err;
X long offset;
X struct stat filestat;
X int compare_chunk ();
X int compare_dir ();
X int no_op ();
X#ifndef __MSDOS__
X dev_t dev;
X ino_t ino;
X#endif
X char *get_dir_contents ();
X long from_oct ();
X
X errno = EPIPE; /* FIXME, remove perrors */
X
X saverec (&head); /* Make sure it sticks around */
X userec (head); /* And go past it in the archive */
X decode_header (head, &hstat, &head_standard, 1); /* Snarf fields */
X
X /* Print the record from 'head' and 'hstat' */
X if (f_verbose)
X {
X if (now_verifying)
X fprintf (msg_file, "Verify ");
X print_header ();
X }
X
X switch (head->header.linkflag)
X {
X
X default:
X msg ("Unknown file type '%c' for %s, diffed as normal file",
X head->header.linkflag, current_file_name);
X /* FALL THRU */
X
X case LF_OLDNORMAL:
X case LF_NORMAL:
X case LF_SPARSE:
X case LF_CONTIG:
X /*
X * Appears to be a file.
X * See if it's really a directory.
X */
X namelen = strlen (current_file_name) - 1;
X if (current_file_name[namelen] == '/')
X goto really_dir;
X
X
X if (do_stat (&filestat))
X {
X if (head->header.isextended)
X skip_extended_headers ();
X skip_file ((long) hstat.st_size);
X different++;
X goto quit;
X }
X
X if (!S_ISREG (filestat.st_mode))
X {
X fprintf (msg_file, "%s: not a regular file\n",
X current_file_name);
X skip_file ((long) hstat.st_size);
X different++;
X goto quit;
X }
X
X filestat.st_mode &= 07777;
X if (filestat.st_mode != hstat.st_mode)
X sigh ("mode");
X if (filestat.st_uid != hstat.st_uid)
X sigh ("uid");
X if (filestat.st_gid != hstat.st_gid)
X sigh ("gid");
X if (filestat.st_mtime != hstat.st_mtime)
X sigh ("mod time");
X if (head->header.linkflag != LF_SPARSE &&
X filestat.st_size != hstat.st_size)
X {
X sigh ("size");
X skip_file ((long) hstat.st_size);
X goto quit;
X }
X
X diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
X
X if (diff_fd < 0 && !f_absolute_paths)
X {
X char tmpbuf[NAMSIZ + 2];
X
X tmpbuf[0] = '/';
X strcpy (&tmpbuf[1], current_file_name);
X diff_fd = open (tmpbuf, O_NDELAY | O_RDONLY);
X }
X if (diff_fd < 0)
X {
X msg_perror ("cannot open %s", current_file_name);
X if (head->header.isextended)
X skip_extended_headers ();
X skip_file ((long) hstat.st_size);
X different++;
X goto quit;
X }
X /*
X * Need to treat sparse files completely differently here.
X */
X if (head->header.linkflag == LF_SPARSE)
X diff_sparse_files (hstat.st_size);
X else
X wantbytes ((long) (hstat.st_size), compare_chunk);
X
X check = close (diff_fd);
X if (check < 0)
X msg_perror ("Error while closing %s", current_file_name);
X
X quit:
X break;
X
X#ifndef __MSDOS__
X case LF_LINK:
X if (do_stat (&filestat))
X break;
X dev = filestat.st_dev;
X ino = filestat.st_ino;
X err = stat (current_link_name, &filestat);
X if (err < 0)
X {
X if (errno == ENOENT)
X {
X fprintf (msg_file, "%s: does not exist\n", current_file_name);
X }
X else
X {
X msg_perror ("cannot stat file %s", current_file_name);
X }
X different++;
X break;
X }
X if (filestat.st_dev != dev || filestat.st_ino != ino)
X {
X fprintf (msg_file, "%s not linked to %s\n", current_file_name, current_link_name);
X break;
X }
X break;
X#endif
X
X#ifdef S_ISLNK
X case LF_SYMLINK:
X {
X char linkbuf[NAMSIZ + 3];
X check = readlink (current_file_name, linkbuf,
X (sizeof linkbuf) - 1);
X
X if (check < 0)
X {
X if (errno == ENOENT)
X {
X fprintf (msg_file,
X "%s: no such file or directory\n",
X current_file_name);
X }
X else
X {
X msg_perror ("cannot read link %s", current_file_name);
X }
X different++;
X break;
X }
X
X linkbuf[check] = '\0'; /* Null-terminate it */
X if (strncmp (current_link_name, linkbuf, check) != 0)
X {
X fprintf (msg_file, "%s: symlink differs\n",
X current_link_name);
X different++;
X }
X }
X break;
X#endif
X
X#ifdef S_IFCHR
X case LF_CHR:
X hstat.st_mode |= S_IFCHR;
X goto check_node;
X#endif
X
X#ifdef S_IFBLK
X /* If local system doesn't support block devices, use default case */
X case LF_BLK:
X hstat.st_mode |= S_IFBLK;
X goto check_node;
X#endif
X
X#ifdef S_ISFIFO
X /* If local system doesn't support FIFOs, use default case */
X case LF_FIFO:
X#ifdef S_IFIFO
X hstat.st_mode |= S_IFIFO;
X#endif
X hstat.st_rdev = 0; /* FIXME, do we need this? */
X goto check_node;
X#endif
X
X check_node:
X /* FIXME, deal with umask */
X if (do_stat (&filestat))
X break;
X if (hstat.st_rdev != filestat.st_rdev)
X {
X fprintf (msg_file, "%s: device numbers changed\n", current_file_name);
X different++;
X break;
X }
X#ifdef S_IFMT
X if (hstat.st_mode != filestat.st_mode)
X#else /* POSIX lossage */
X if ((hstat.st_mode & 07777) != (filestat.st_mode & 07777))
X#endif
X {
X fprintf (msg_file, "%s: mode or device-type changed\n", current_file_name);
X different++;
X break;
X }
X break;
X
X case LF_DUMPDIR:
X data = diff_dir = get_dir_contents (current_file_name, 0);
X if (data)
X {
X wantbytes ((long) (hstat.st_size), compare_dir);
X free (data);
X }
X else
X wantbytes ((long) (hstat.st_size), no_op);
X /* FALL THROUGH */
X
X case LF_DIR:
X /* Check for trailing / */
X namelen = strlen (current_file_name) - 1;
X really_dir:
X while (namelen && current_file_name[namelen] == '/')
X current_file_name[namelen--] = '\0'; /* Zap / */
X
X if (do_stat (&filestat))
X break;
X if (!S_ISDIR (filestat.st_mode))
X {
X fprintf (msg_file, "%s is no longer a directory\n", current_file_name);
X different++;
X break;
X }
X if ((filestat.st_mode & 07777) != (hstat.st_mode & 07777))
X sigh ("mode");
X break;
X
X case LF_VOLHDR:
X break;
X
X case LF_MULTIVOL:
X namelen = strlen (current_file_name) - 1;
X if (current_file_name[namelen] == '/')
X goto really_dir;
X
X if (do_stat (&filestat))
X break;
X
X if (!S_ISREG (filestat.st_mode))
X {
X fprintf (msg_file, "%s: not a regular file\n",
X current_file_name);
X skip_file ((long) hstat.st_size);
X different++;
X break;
X }
X
X filestat.st_mode &= 07777;
X offset = from_oct (1 + 12, head->header.offset);
X if (filestat.st_size != hstat.st_size + offset)
X {
X sigh ("size");
X skip_file ((long) hstat.st_size);
X different++;
X break;
X }
X
X diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
X
X if (diff_fd < 0)
X {
X msg_perror ("cannot open file %s", current_file_name);
X skip_file ((long) hstat.st_size);
X different++;
X break;
X }
X err = lseek (diff_fd, offset, 0);
X if (err != offset)
X {
X msg_perror ("cannot seek to %ld in file %s", offset, current_file_name);
X different++;
X break;
X }
X
X wantbytes ((long) (hstat.st_size), compare_chunk);
X
X check = close (diff_fd);
X if (check < 0)
X {
X msg_perror ("Error while closing %s", current_file_name);
X }
X break;
X
X }
X
X /* We don't need to save it any longer. */
X saverec ((union record **) 0);/* Unsave it */
X}
X
Xint
Xcompare_chunk (bytes, buffer)
X long bytes;
X char *buffer;
X{
X int err;
X
X err = read (diff_fd, diff_buf, bytes);
X if (err != bytes)
X {
X if (err < 0)
X {
X msg_perror ("can't read %s", current_file_name);
X }
X else
X {
X fprintf (msg_file, "%s: could only read %d of %d bytes\n", current_file_name, err, bytes);
X }
X different++;
X return -1;
X }
X if (bcmp (buffer, diff_buf, bytes))
X {
X fprintf (msg_file, "%s: data differs\n", current_file_name);
X different++;
X return -1;
X }
X return 0;
X}
X
Xint
Xcompare_dir (bytes, buffer)
X long bytes;
X char *buffer;
X{
X if (bcmp (buffer, diff_dir, bytes))
X {
X fprintf (msg_file, "%s: data differs\n", current_file_name);
X different++;
X return -1;
X }
X diff_dir += bytes;
X return 0;
X}
X
X/*
X * Sigh about something that differs.
X */
Xvoid
Xsigh (what)
X char *what;
X{
X
X fprintf (msg_file, "%s: %s differs\n",
X current_file_name, what);
X}
X
Xvoid
Xverify_volume ()
X{
X int status;
X#ifdef MTIOCTOP
X struct mtop t;
X int er;
X#endif
X
X if (!diff_buf)
X diff_init ();
X#ifdef MTIOCTOP
X t.mt_op = MTBSF;
X t.mt_count = 1;
X if ((er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
X {
X if (errno != EIO || (er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
X {
X#endif
X if (rmtlseek (archive, 0L, 0) != 0)
X {
X /* Lseek failed. Try a different method */
X msg_perror ("Couldn't rewind archive file for verify");
X return;
X }
X#ifdef MTIOCTOP
X }
X }
X#endif
X ar_reading = 1;
X now_verifying = 1;
X fl_read ();
X for (;;)
X {
X status = read_header ();
X if (status == 0)
X {
X unsigned n;
X
X n = 0;
X do
X {
X n++;
X status = read_header ();
X }
X while (status == 0);
X msg ("VERIFY FAILURE: %d invalid header%s detected!", n, n == 1 ? "" : "s");
X }
X if (status == 2 || status == EOF)
X break;
X diff_archive ();
X }
X ar_reading = 0;
X now_verifying = 0;
X
X}
X
Xint
Xdo_stat (statp)
X struct stat *statp;
X{
X int err;
X
X err = f_follow_links ? stat (current_file_name, statp) : lstat (current_file_name, statp);
X if (err < 0)
X {
X if (errno == ENOENT)
X {
X fprintf (msg_file, "%s: does not exist\n", current_file_name);
X }
X else
X msg_perror ("can't stat file %s", current_file_name);
X /* skip_file((long)hstat.st_size);
X different++;*/
X return 1;
X }
X else
X return 0;
X}
X
X/*
X * JK
X * Diff'ing a sparse file with its counterpart on the tar file is a
X * bit of a different story than a normal file. First, we must know
X * what areas of the file to skip through, i.e., we need to contruct
X * a sparsearray, which will hold all the information we need. We must
X * compare small amounts of data at a time as we find it.
X */
X
Xvoid
Xdiff_sparse_files (filesize)
X int filesize;
X
X{
X int sparse_ind = 0;
X char *buf;
X int buf_size = RECORDSIZE;
X union record *datarec;
X int err;
X long numbytes;
X /* int amt_read = 0;*/
X int size = filesize;
X
X buf = (char *) ck_malloc (buf_size * sizeof (char));
X
X fill_in_sparse_array ();
X
X
X while (size > 0)
X {
X datarec = findrec ();
X if (!sparsearray[sparse_ind].numbytes)
X break;
X
X /*
X * 'numbytes' is nicer to write than
X * 'sparsearray[sparse_ind].numbytes' all the time ...
X */
X numbytes = sparsearray[sparse_ind].numbytes;
X
X lseek (diff_fd, sparsearray[sparse_ind].offset, 0);
X /*
X * take care to not run out of room in our buffer
X */
X while (buf_size < numbytes)
X {
X buf = (char *) ck_realloc (buf, buf_size * 2 * sizeof (char));
X buf_size *= 2;
X }
X while (numbytes > RECORDSIZE)
X {
X if ((err = read (diff_fd, buf, RECORDSIZE)) != RECORDSIZE)
X {
X if (err < 0)
X msg_perror ("can't read %s", current_file_name);
X else
X fprintf (msg_file, "%s: could only read %d of %d bytes\n",
X current_file_name, err, numbytes);
X break;
X }
X if (bcmp (buf, datarec->charptr, RECORDSIZE))
X {
X different++;
X break;
X }
X numbytes -= err;
X size -= err;
X userec (datarec);
X datarec = findrec ();
X }
X if ((err = read (diff_fd, buf, numbytes)) != numbytes)
X {
X if (err < 0)
X msg_perror ("can't read %s", current_file_name);
X else
X fprintf (msg_file, "%s: could only read %d of %d bytes\n",
X current_file_name, err, numbytes);
X break;
X }
X
X if (bcmp (buf, datarec->charptr, numbytes))
X {
X different++;
X break;
X }
X /* amt_read += numbytes;
X if (amt_read >= RECORDSIZE) {
X amt_read = 0;
X userec(datarec);
X datarec = findrec();
X }*/
X userec (datarec);
X sparse_ind++;
X size -= numbytes;
X }
X /*
X * if the number of bytes read isn't the
X * number of bytes supposedly in the file,
X * they're different
X */
X /* if (amt_read != filesize)
X different++;*/
X userec (datarec);
X free (sparsearray);
X if (different)
X fprintf (msg_file, "%s: data differs\n", current_file_name);
X
X}
X
X/*
X * JK
X * This routine should be used more often than it is ... look into
X * that. Anyhow, what it does is translate the sparse information
X * on the header, and in any subsequent extended headers, into an
X * array of structures with true numbers, as opposed to character
X * strings. It simply makes our life much easier, doing so many
X * comparisong and such.
X */
Xvoid
Xfill_in_sparse_array ()
X{
X int ind;
X
X /*
X * allocate space for our scratch space; it's initially
X * 10 elements long, but can change in this routine if
X * necessary
X */
X sp_array_size = 10;
X sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
X
X /*
X * there are at most five of these structures in the header
X * itself; read these in first
X */
X for (ind = 0; ind < SPARSE_IN_HDR; ind++)
X {
X if (!head->header.sp[ind].numbytes)
X break;
X sparsearray[ind].offset =
X from_oct (1 + 12, head->header.sp[ind].offset);
X sparsearray[ind].numbytes =
X from_oct (1 + 12, head->header.sp[ind].numbytes);
X }
X /*
X * if the header's extended, we gotta read in exhdr's till
X * we're done
X */
X if (head->header.isextended)
X {
X /* how far into the sparsearray we are 'so far' */
X static int so_far_ind = SPARSE_IN_HDR;
X union record *exhdr;
X
X for (;;)
X {
X exhdr = findrec ();
X for (ind = 0; ind < SPARSE_EXT_HDR; ind++)
X {
X if (ind + so_far_ind > sp_array_size - 1)
X {
X /*
X * we just ran out of room in our
X * scratch area - realloc it
X */
X sparsearray = (struct sp_array *)
X ck_realloc (sparsearray,
X sp_array_size * 2 * sizeof (struct sp_array));
X sp_array_size *= 2;
X }
X /*
X * convert the character strings into longs
X */
X sparsearray[ind + so_far_ind].offset =
X from_oct (1 + 12, exhdr->ext_hdr.sp[ind].offset);
X sparsearray[ind + so_far_ind].numbytes =
X from_oct (1 + 12, exhdr->ext_hdr.sp[ind].numbytes);
X }
X /*
X * if this is the last extended header for this
X * file, we can stop
X */
X if (!exhdr->ext_hdr.isextended)
X break;
X else
X {
X so_far_ind += SPARSE_EXT_HDR;
X userec (exhdr);
X }
X }
X /* be sure to skip past the last one */
X userec (exhdr);
X }
X}
END_OF_FILE
if test 16692 -ne `wc -c <'diffarch.c'`; then
echo shar: \"'diffarch.c'\" unpacked with wrong size!
fi
# end of 'diffarch.c'
fi
if test -f 'port.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'port.c'\"
else
echo shar: Extracting \"'port.c'\" \(25899 characters\)
sed "s/^X//" >'port.c' <<'END_OF_FILE'
X/* Supporting routines which may sometimes be missing.
X Copyright (C) 1988, 1992 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <signal.h>
X#include <errno.h>
X#ifndef STDC_HEADERS
Xextern int errno;
X#endif
X
X#ifdef BSD42
X#include <sys/file.h>
X#else
X#ifndef V7
X#include <fcntl.h>
X#endif
X#endif
X
X#include "tar.h"
X#include "port.h"
X
Xextern long baserec;
X
X/* All machine-dependent #ifdefs should appear here, instead of
X being scattered through the file. For UN*X systems, it is better to
X figure out what is needed in the configure script, for most of the
X features. */
X
X#ifdef __MSDOS__
Xchar TTY_NAME[] = "con";
X#define HAVE_STRSTR
X#define HAVE_RENAME
X#define HAVE_MKDIR
X#else
Xchar TTY_NAME[] = "/dev/tty";
X#endif
X
X/* End of system-dependent #ifdefs */
X
X
X#ifndef HAVE_VALLOC
X/*
X * valloc() does a malloc() on a page boundary. On some systems,
X * this can make large block I/O more efficient.
X */
Xchar *
Xvalloc (size)
X unsigned size;
X{
X return (malloc (size));
X}
X
X#endif /* !HAVE_VALLOC */
X
X#ifndef HAVE_MKDIR
X/*
X * Written by Robert Rother, Mariah Corporation, August 1985.
X *
X * If you want it, it's yours. All I ask in return is that if you
X * figure out how to do this in a Bourne Shell script you send me
X * a copy.
X * sdcsvax!rmr or rmr@uscd
X *
X * Severely hacked over by John Gilmore to make a 4.2BSD compatible
X * subroutine. 11Mar86; hoptoad!gnu
X *
X * Modified by rmtodd@uokmax 6-28-87 -- when making an already existing dir,
X * subroutine didn't return EEXIST. It does now.
X */
X
X/*
X * Make a directory.
X */
Xint
Xmkdir (dpath, dmode)
X char *dpath;
X int dmode;
X{
X int cpid, status;
X struct stat statbuf;
X
X if (stat (dpath, &statbuf) == 0)
X {
X errno = EEXIST; /* Stat worked, so it already exists */
X return -1;
X }
X
X /* If stat fails for a reason other than non-existence, return error */
X if (errno != ENOENT)
X return -1;
X
X switch (cpid = fork ())
X {
X
X case -1: /* Error in fork() */
X return (-1); /* Errno is set already */
X
X case 0: /* Child process */
X /*
X * Cheap hack to set mode of new directory. Since this
X * child process is going away anyway, we zap its umask.
X * FIXME, this won't suffice to set SUID, SGID, etc. on this
X * directory. Does anybody care?
X */
X status = umask (0); /* Get current umask */
X status = umask (status | (0777 & ~dmode)); /* Set for mkdir */
X execl ("/bin/mkdir", "mkdir", dpath, (char *) 0);
X _exit (-1); /* Can't exec /bin/mkdir */
X
X default: /* Parent process */
X while (cpid != wait (&status)); /* Wait for kid to finish */
X }
X
X if (WIFSIGNALED (status) || WEXITSTATUS (status) != 0)
X {
X errno = EIO; /* We don't know why, but */
X return -1; /* /bin/mkdir failed */
X }
X
X return 0;
X}
X
Xint
Xrmdir (dpath)
X char *dpath;
X{
X int cpid, status;
X struct stat statbuf;
X
X if (stat (dpath, &statbuf) != 0)
X {
X /* Stat just set errno. We don't have to */
X return -1;
X }
X
X switch (cpid = fork ())
X {
X
X case -1: /* Error in fork() */
X return (-1); /* Errno is set already */
X
X case 0: /* Child process */
X execl ("/bin/rmdir", "rmdir", dpath, (char *) 0);
X _exit (-1); /* Can't exec /bin/mkdir */
X
X default: /* Parent process */
X while (cpid != wait (&status)); /* Wait for kid to finish */
X }
X
X if (WIFSIGNALED (status) || WEXITSTATUS (status) != 0)
X {
X errno = EIO; /* We don't know why, but */
X return -1; /* /bin/mkdir failed */
X }
X
X return 0;
X}
X
X#endif /* !HAVE_MKDIR */
X
X#ifndef HAVE_RENAME
X/* Rename file FROM to file TO.
X Return 0 if successful, -1 if not. */
X
Xint
Xrename (from, to)
X char *from;
X char *to;
X{
X struct stat from_stats;
X
X if (stat (from, &from_stats))
X return -1;
X
X if (unlink (to) && errno != ENOENT)
X return -1;
X
X if (link (from, to))
X return -1;
X
X if (unlink (from) && errno != ENOENT)
X {
X unlink (to);
X return -1;
X }
X
X return 0;
X}
X
X#endif /* !HAVE_RENAME */
X
X#ifdef minix
X/* Minix has bcopy but not bzero, and no memset. Thanks, Andy. */
Xvoid
Xbzero (s1, n)
X register char *s1;
X register int n;
X{
X while (n--)
X *s1++ = '\0';
X}
X
X/* It also has no bcmp() */
Xint
Xbcmp (s1, s2, n)
X register char *s1, *s2;
X register int n;
X{
X for (; n--; ++s1, ++s2)
X {
X if (*s1 != *s2)
X return *s1 - *s2;
X }
X return 0;
X}
X
X/*
X * Groan, Minix doesn't have execlp either!
X *
X * execlp(file,arg0,arg1...argn,(char *)NULL)
X * exec a program, automatically searching for the program through
X * all the directories on the PATH.
X *
X * This version is naive about variable argument lists, it assumes
X * a straightforward C calling sequence. If your system has odd stacks
X * *and* doesn't have execlp, YOU get to fix it.
X */
Xint
Xexeclp (filename, arg0)
X char *filename, *arg0;
X{
X register char *p, *path;
X register char *fnbuffer;
X char **argstart = &arg0;
X struct stat statbuf;
X extern char **environ;
X
X if ((p = getenv ("PATH")) == NULL)
X {
X /* couldn't find path variable -- try to exec given filename */
X return execve (filename, argstart, environ);
X }
X
X /*
X * make a place to build the filename. We malloc larger than we
X * need, but we know it will fit in this.
X */
X fnbuffer = malloc (strlen (p) + 1 + strlen (filename));
X if (fnbuffer == NULL)
X {
X errno = ENOMEM;
X return -1;
X }
X
X /*
X * try each component of the path to see if the file's there
X * and executable.
X */
X for (path = p; path; path = p)
X {
X /* construct full path name to try */
X if ((p = index (path, ':')) == NULL)
X {
X strcpy (fnbuffer, path);
X }
X else
X {
X strncpy (fnbuffer, path, p - path);
X fnbuffer[p - path] = '\0';
X p++; /* Skip : for next time */
X }
X if (strlen (fnbuffer) != 0)
X strcat (fnbuffer, "/");
X strcat (fnbuffer, filename);
X
X /* check to see if file is there and is a normal file */
X if (stat (fnbuffer, &statbuf) < 0)
X {
X if (errno == ENOENT)
X continue; /* file not there,keep on looking */
X else
X goto fail; /* failed for some reason, return */
X }
X if (!S_ISREG (statbuf.st_mode))
X continue;
X
X if (execve (fnbuffer, argstart, environ) < 0
X && errno != ENOENT
X && errno != ENOEXEC)
X {
X /* failed, for some other reason besides "file
X * not found" or "not a.out format"
X */
X goto fail;
X }
X
X /*
X * If we got error ENOEXEC, the file is executable but is
X * not an object file. Try to execute it as a shell script,
X * returning error if we can't execute /bin/sh.
X *
X * FIXME, this code is broken in several ways. Shell
X * scripts should not in general be executed by the user's
X * SHELL variable program. On more mature systems, the
X * script can specify with #!/bin/whatever. Also, this
X * code clobbers argstart[-1] if the exec of the shell
X * fails.
X */
X if (errno == ENOEXEC)
X {
X char *shell;
X
X /* Try to execute command "sh arg0 arg1 ..." */
X if ((shell = getenv ("SHELL")) == NULL)
X shell = "/bin/sh";
X argstart[-1] = shell;
X argstart[0] = fnbuffer;
X execve (shell, &argstart[-1], environ);
X goto fail; /* Exec didn't work */
X }
X
X /*
X * If we succeeded, the execve() doesn't return, so we
X * can only be here is if the file hasn't been found yet.
X * Try the next place on the path.
X */
X }
X
X /* all attempts failed to locate the file. Give up. */
X errno = ENOENT;
X
Xfail:
X free (fnbuffer);
X return -1;
X}
X
X#endif /* minix */
X
X
X#ifdef EMUL_OPEN3
X#include "open3.h"
X/*
X * open3 -- routine to emulate the 3-argument open system
X * call that is present in most modern Unix systems.
X * This version attempts to support all the flag bits except for O_NDELAY
X * and O_APPEND, which are silently ignored. The emulation is not as efficient
X * as the real thing (at worst, 4 system calls instead of one), but there's
X * not much I can do about that.
X *
X * Written 6/10/87 by rmtodd@uokmax
X *
X * open3(path, flag, mode)
X * Attempts to open the file specified by
X * the given pathname. The following flag bits (#defined in tar.h)
X * specify options to the routine:
X * O_RDONLY file open for read only
X * O_WRONLY file open for write only
X * O_RDWR file open for both read & write
X * (Needless to say, you should only specify one of the above).
X * O_CREAT file is created with specified mode if it needs to be.
X * O_TRUNC if file exists, it is truncated to 0 bytes
X * O_EXCL used with O_CREAT--routine returns error if file exists
X * Function returns file descriptor if successful, -1 and errno if not.
X */
X
X/*
X * array to give arguments to access for various modes
X * FIXME, this table depends on the specific integer values of O_XXX,
X * and also contains integers (args to 'access') that should be #define's.
X */
Xstatic int modes[] =
X{
X 04, /* O_RDONLY */
X 02, /* O_WRONLY */
X 06, /* O_RDWR */
X 06, /* invalid but we'd better cope -- O_WRONLY+O_RDWR */
X};
X
X/* Shut off the automatic emulation of open(), we'll need it. */
X#undef open
X
Xint
Xopen3 (path, flags, mode)
X char *path;
X int flags, mode;
X{
X int exists = 1;
X int call_creat = 0;
X int fd;
X /*
X * We actually do the work by calling the open() or creat() system
X * call, depending on the flags. Call_creat is true if we will use
X * creat(), false if we will use open().
X */
X
X /*
X * See if the file exists and is accessible in the requested mode.
X *
X * Strictly speaking we shouldn't be using access, since access checks
X * against real uid, and the open call should check against euid.
X * Most cases real uid == euid, so it won't matter. FIXME.
X * FIXME, the construction "flags & 3" and the modes table depends
X * on the specific integer values of the O_XXX #define's. Foo!
X */
X if (access (path, modes[flags & 3]) < 0)
X {
X if (errno == ENOENT)
X {
X /* the file does not exist */
X exists = 0;
X }
X else
X {
X /* probably permission violation */
X if (flags & O_EXCL)
X {
X /* Oops, the file exists, we didn't want it. */
X /* No matter what the error, claim EEXIST. */
X errno = EEXIST;
X }
X return -1;
X }
X }
X
X /* if we have the O_CREAT bit set, check for O_EXCL */
X if (flags & O_CREAT)
X {
X if ((flags & O_EXCL) && exists)
X {
X /* Oops, the file exists and we didn't want it to. */
X errno = EEXIST;
X return -1;
X }
X /*
X * If the file doesn't exist, be sure to call creat() so that
X * it will be created with the proper mode.
X */
X if (!exists)
X call_creat = 1;
X }
X else
X {
X /* If O_CREAT isn't set and the file doesn't exist, error. */
X if (!exists)
X {
X errno = ENOENT;
X return -1;
X }
X }
X
X /*
X * If the O_TRUNC flag is set and the file exists, we want to call
X * creat() anyway, since creat() guarantees that the file will be
X * truncated and open()-for-writing doesn't.
X * (If the file doesn't exist, we're calling creat() anyway and the
X * file will be created with zero length.)
X */
X if ((flags & O_TRUNC) && exists)
X call_creat = 1;
X /* actually do the call */
X if (call_creat)
X {
X /*
X * call creat. May have to close and reopen the file if we
X * want O_RDONLY or O_RDWR access -- creat() only gives
X * O_WRONLY.
X */
X fd = creat (path, mode);
X if (fd < 0 || (flags & O_WRONLY))
X return fd;
X if (close (fd) < 0)
X return -1;
X /* Fall out to reopen the file we've created */
X }
X
X /*
X * calling old open, we strip most of the new flags just in case.
X */
X return open (path, flags & (O_RDONLY | O_WRONLY | O_RDWR | O_BINARY));
X}
X
X#endif /* EMUL_OPEN3 */
X
X#ifndef HAVE_MKNOD
X#ifdef __MSDOS__
Xtypedef int dev_t;
X#endif
X/* Fake mknod by complaining */
Xint
Xmknod (path, mode, dev)
X char *path;
X unsigned short mode;
X dev_t dev;
X{
X int fd;
X
X errno = ENXIO; /* No such device or address */
X return -1; /* Just give an error */
X}
X
X/* Fake links by copying */
Xint
Xlink (path1, path2)
X char *path1;
X char *path2;
X{
X char buf[256];
X int ifd, ofd;
X int nrbytes;
X int nwbytes;
X
X fprintf (stderr, "%s: %s: cannot link to %s, copying instead\n",
X tar, path1, path2);
X if ((ifd = open (path1, O_RDONLY | O_BINARY)) < 0)
X return -1;
X if ((ofd = creat (path2, 0666)) < 0)
X return -1;
X setmode (ofd, O_BINARY);
X while ((nrbytes = read (ifd, buf, sizeof (buf))) > 0)
X {
X if ((nwbytes = write (ofd, buf, nrbytes)) != nrbytes)
X {
X nrbytes = -1;
X break;
X }
X }
X /* Note use of "|" rather than "||" below: we want to close
X * the files even if an error occurs.
X */
X if ((nrbytes < 0) | (0 != close (ifd)) | (0 != close (ofd)))
X {
X unlink (path2);
X return -1;
X }
X return 0;
X}
X
X/* everyone owns everything on MS-DOS (or is it no one owns anything?) */
Xint
Xchown (path, uid, gid)
X char *path;
X int uid;
X int gid;
X{
X return 0;
X}
X
Xint
Xgeteuid ()
X{
X return 0;
X}
X
X#endif /* !HAVE_MKNOD */
X
X#ifdef __TURBOC__
X#include <time.h>
X#include <fcntl.h>
X#include <io.h>
X
Xstruct utimbuf
X{
X time_t actime; /* Access time. */
X time_t modtime; /* Modification time. */
X};
X
Xint
Xutime (char *filename, struct utimbuf *utb)
X{
X struct tm *tm;
X struct ftime filetime;
X time_t when;
X int fd;
X int status;
X
X if (utb == 0)
X when = time (0);
X else
X when = utb->modtime;
X
X fd = _open (filename, O_RDWR);
X if (fd == -1)
X return -1;
X
X tm = localtime (&when);
X if (tm->tm_year < 80)
X filetime.ft_year = 0;
X else
X filetime.ft_year = tm->tm_year - 80;
X filetime.ft_month = tm->tm_mon + 1;
X filetime.ft_day = tm->tm_mday;
X if (tm->tm_hour < 0)
X filetime.ft_hour = 0;
X else
X filetime.ft_hour = tm->tm_hour;
X filetime.ft_min = tm->tm_min;
X filetime.ft_tsec = tm->tm_sec / 2;
X
X status = setftime (fd, &filetime);
X _close (fd);
X return status;
X}
X
X#endif /* __TURBOC__ */
X
X/* Stash argv[0] here so panic will know what the program is called */
Xchar *myname = 0;
X
Xvoid
Xpanic (s)
X char *s;
X{
X if (myname)
X fprintf (stderr, "%s:", myname);
X fprintf (stderr, s);
X putc ('\n', stderr);
X exit (12);
X}
X
X
XPTR
Xck_malloc (size)
X size_t size;
X{
X PTR ret;
X
X if (!size)
X size++;
X ret = malloc (size);
X if (ret == 0)
X panic ("Couldn't allocate memory");
X return ret;
X}
X
X/* Used by alloca.c and bison.simple. */
Xchar *
Xxmalloc (size)
X size_t size;
X{
X return (char *) ck_malloc (size);
X}
X
XPTR
Xck_realloc (ptr, size)
X PTR ptr;
X size_t size;
X{
X PTR ret;
X
X if (!ptr)
X ret = ck_malloc (size);
X else
X ret = realloc (ptr, size);
X if (ret == 0)
X panic ("Couldn't re-allocate memory");
X return ret;
X}
X
X/* Implement a variable sized buffer of 'stuff'. We don't know what it is,
X nor do we care, as long as it doesn't mind being aligned on a char boundry.
X */
X
Xstruct buffer
X {
X int allocated;
X int length;
X char *b;
X };
X
X#define MIN_ALLOCATE 50
X
Xchar *
Xinit_buffer ()
X{
X struct buffer *b;
X
X b = (struct buffer *) ck_malloc (sizeof (struct buffer));
X b->allocated = MIN_ALLOCATE;
X b->b = (char *) ck_malloc (MIN_ALLOCATE);
X b->length = 0;
X return (char *) b;
X}
X
Xvoid
Xflush_buffer (bb)
X char *bb;
X{
X struct buffer *b;
X
X b = (struct buffer *) bb;
X free (b->b);
X b->b = 0;
X b->allocated = 0;
X b->length = 0;
X free ((void *) b);
X}
X
Xvoid
Xadd_buffer (bb, p, n)
X char *bb;
X char *p;
X int n;
X{
X struct buffer *b;
X
X b = (struct buffer *) bb;
X if (b->length + n > b->allocated)
X {
X b->allocated = b->length + n + MIN_ALLOCATE;
X b->b = (char *) ck_realloc (b->b, b->allocated);
X }
X bcopy (p, b->b + b->length, n);
X b->length += n;
X}
X
Xchar *
Xget_buffer (bb)
X char *bb;
X{
X struct buffer *b;
X
X b = (struct buffer *) bb;
X return b->b;
X}
X
Xchar *
Xmerge_sort (list, n, off, cmp)
X char *list;
X int (*cmp) ();
X unsigned n;
X int off;
X{
X char *ret;
X
X char *alist, *blist;
X unsigned alength, blength;
X
X char *tptr;
X int tmp;
X char **prev;
X#define NEXTOF(ptr) (* ((char **)(((char *)(ptr))+off) ) )
X if (n == 1)
X return list;
X if (n == 2)
X {
X if ((*cmp) (list, NEXTOF (list)) > 0)
X {
X ret = NEXTOF (list);
X NEXTOF (ret) = list;
X NEXTOF (list) = 0;
X return ret;
X }
X return list;
X }
X alist = list;
X alength = (n + 1) / 2;
X blength = n / 2;
X for (tptr = list, tmp = (n - 1) / 2; tmp; tptr = NEXTOF (tptr), tmp--)
X ;
X blist = NEXTOF (tptr);
X NEXTOF (tptr) = 0;
X
X alist = merge_sort (alist, alength, off, cmp);
X blist = merge_sort (blist, blength, off, cmp);
X prev = &ret;
X for (; alist && blist;)
X {
X if ((*cmp) (alist, blist) < 0)
X {
X tptr = NEXTOF (alist);
X *prev = alist;
X prev = &(NEXTOF (alist));
X alist = tptr;
X }
X else
X {
X tptr = NEXTOF (blist);
X *prev = blist;
X prev = &(NEXTOF (blist));
X blist = tptr;
X }
X }
X if (alist)
X *prev = alist;
X else
X *prev = blist;
X
X return ret;
X}
X
Xvoid
Xck_close (fd)
X int fd;
X{
X if (close (fd) < 0)
X {
X msg_perror ("can't close a file #%d", fd);
X exit (EX_SYSTEM);
X }
X}
X
X#include <ctype.h>
X
X/* Quote_copy_string is like quote_string, but instead of modifying the
X string in place, it malloc-s a copy of the string, and returns that.
X If the string does not have to be quoted, it returns the NULL string.
X The allocated copy can, of course, be freed with free() after the
X caller is done with it.
X */
Xchar *
Xquote_copy_string (string)
X char *string;
X{
X char *from_here;
X char *to_there = 0;
X char *copy_buf = 0;
X int c;
X int copying = 0;
X
X from_here = string;
X while (*from_here)
X {
X c = *from_here++;
X if (c == '\\')
X {
X if (!copying)
X {
X int n;
X
X n = (from_here - string) - 1;
X copying++;
X copy_buf = (char *) malloc (n + 5 + strlen (from_here) * 4);
X if (!copy_buf)
X return 0;
X bcopy (string, copy_buf, n);
X to_there = copy_buf + n;
X }
X *to_there++ = '\\';
X *to_there++ = '\\';
X }
X else if (isprint (c))
X {
X if (copying)
X *to_there++ = c;
X }
X else
X {
X if (!copying)
X {
X int n;
X
X n = (from_here - string) - 1;
X copying++;
X copy_buf = (char *) malloc (n + 5 + strlen (from_here) * 4);
X if (!copy_buf)
X return 0;
X bcopy (string, copy_buf, n);
X to_there = copy_buf + n;
X }
X *to_there++ = '\\';
X if (c == '\n')
X *to_there++ = 'n';
X else if (c == '\t')
X *to_there++ = 't';
X else if (c == '\f')
X *to_there++ = 'f';
X else if (c == '\b')
X *to_there++ = 'b';
X else if (c == '\r')
X *to_there++ = 'r';
X else if (c == '\177')
X *to_there++ = '?';
X else
X {
X to_there[0] = (c >> 6) + '0';
X to_there[1] = ((c >> 3) & 07) + '0';
X to_there[2] = (c & 07) + '0';
X to_there += 3;
X }
X }
X }
X if (copying)
X {
X *to_there = '\0';
X return copy_buf;
X }
X return (char *) 0;
X}
X
X
X/* Un_quote_string takes a quoted c-string (like those produced by
X quote_string or quote_copy_string and turns it back into the
X un-quoted original. This is done in place.
X */
X
X/* There is no un-quote-copy-string. Write it yourself */
X
Xchar *
Xun_quote_string (string)
X char *string;
X{
X char *ret;
X char *from_here;
X char *to_there;
X int tmp;
X
X ret = string;
X to_there = string;
X from_here = string;
X while (*from_here)
X {
X if (*from_here != '\\')
X {
X if (from_here != to_there)
X *to_there++ = *from_here++;
X else
X from_here++, to_there++;
X continue;
X }
X switch (*++from_here)
X {
X case '\\':
X *to_there++ = *from_here++;
X break;
X case 'n':
X *to_there++ = '\n';
X from_here++;
X break;
X case 't':
X *to_there++ = '\t';
X from_here++;
X break;
X case 'f':
X *to_there++ = '\f';
X from_here++;
X break;
X case 'b':
X *to_there++ = '\b';
X from_here++;
X break;
X case 'r':
X *to_there++ = '\r';
X from_here++;
X break;
X case '?':
X *to_there++ = 0177;
X from_here++;
X break;
X case '0':
X case '1':
X case '2':
X case '3':
X case '4':
X case '5':
X case '6':
X case '7':
X tmp = *from_here - '0';
X from_here++;
X if (*from_here < '0' || *from_here > '7')
X {
X *to_there++ = tmp;
X break;
X }
X tmp = tmp * 8 + *from_here - '0';
X from_here++;
X if (*from_here < '0' || *from_here > '7')
X {
X *to_there++ = tmp;
X break;
X }
X tmp = tmp * 8 + *from_here - '0';
X from_here++;
X *to_there = tmp;
X break;
X default:
X ret = 0;
X *to_there++ = '\\';
X *to_there++ = *from_here++;
X break;
X }
X }
X if (*to_there)
X *to_there++ = '\0';
X return ret;
X}
X
X#ifndef __MSDOS__
Xvoid
Xck_pipe (pipes)
X int *pipes;
X{
X if (pipe (pipes) < 0)
X {
X msg_perror ("can't open a pipe");
X exit (EX_SYSTEM);
X }
X}
X#endif /* !__MSDOS__ */
X
X#ifndef HAVE_STRSTR
X/*
X * strstr - find first occurrence of wanted in s
X */
X
Xchar * /* found string, or NULL if none */
Xstrstr (s, wanted)
X char *s;
X char *wanted;
X{
X register char *scan;
X register size_t len;
X register char firstc;
X
X if (*wanted == '\0')
X return (char *) 0;
X /*
X * The odd placement of the two tests is so "" is findable.
X * Also, we inline the first char for speed.
X * The ++ on scan has been moved down for optimization.
X */
X firstc = *wanted;
X len = strlen (wanted);
X for (scan = s; *scan != firstc || strncmp (scan, wanted, len) != 0;)
X if (*scan++ == '\0')
X return (char *) 0;
X return scan;
X}
X
X#endif /* !HAVE_STRSTR */
X
X#ifndef HAVE_FTRUNCATE
X
X#ifdef F_CHSIZE
Xint
Xftruncate (fd, length)
X int fd;
X off_t length;
X{
X return fcntl (fd, F_CHSIZE, length);
X}
X
X#else /* !F_CHSIZE */
X#ifdef F_FREESP
X/* code courtesy of William Kucharski, kucharsk@Solbourne.com */
X
Xint
Xftruncate (fd, length)
X int fd; /* file descriptor */
X off_t length; /* length to set file to */
X{
X struct flock fl;
X
X fl.l_whence = 0;
X fl.l_len = 0;
X fl.l_start = length;
X fl.l_type = F_WRLCK; /* write lock on file space */
X
X /*
X * This relies on the UNDOCUMENTED F_FREESP argument to
X * fcntl(2), which truncates the file so that it ends at the
X * position indicated by fl.l_start.
X *
X * Will minor miracles never cease?
X */
X
X if (fcntl (fd, F_FREESP, &fl) < 0)
X return -1;
X
X return 0;
X}
X
X#else /* !F_FREESP */
X
Xint
Xftruncate (fd, length)
X int fd;
X off_t length;
X{
X errno = EIO;
X return -1;
X}
X
X#endif /* !F_FREESP */
X#endif /* !F_CHSIZE */
X#endif /* !HAVE_FTRUNCATE */
X
X
Xextern FILE *msg_file;
X
X#if defined (HAVE_VPRINTF) && __STDC__
X#include <stdarg.h>
X
Xvoid
Xmsg (char *str,...)
X{
X va_list args;
X
X va_start (args, str);
X fflush (msg_file);
X fprintf (stderr, "%s: ", tar);
X if (f_sayblock)
X fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
X vfprintf (stderr, str, args);
X va_end (args);
X putc ('\n', stderr);
X fflush (stderr);
X}
X
Xvoid
Xmsg_perror (char *str,...)
X{
X va_list args;
X int save_e;
X
X save_e = errno;
X fflush (msg_file);
X fprintf (stderr, "%s: ", tar);
X if (f_sayblock)
X fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
X va_start (args, str);
X vfprintf (stderr, str, args);
X va_end (args);
X errno = save_e;
X perror (" ");
X fflush (stderr);
X}
X
X#endif /* HAVE_VPRINTF and __STDC__ */
X
X#if defined(HAVE_VPRINTF) && !__STDC__
X#include <varargs.h>
Xvoid
Xmsg (str, va_alist)
X char *str;
X va_dcl
X{
X va_list args;
X
X fflush (msg_file);
X fprintf (stderr, "%s: ", tar);
X if (f_sayblock)
X fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
X va_start (args);
X vfprintf (stderr, str, args);
X va_end (args);
X putc ('\n', stderr);
X fflush (stderr);
X}
X
Xvoid
Xmsg_perror (str, va_alist)
X char *str;
X va_dcl
X{
X va_list args;
X int save_e;
X
X save_e = errno;
X fflush (msg_file);
X fprintf (stderr, "%s: ", tar);
X if (f_sayblock)
X fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
X va_start (args);
X vfprintf (stderr, str, args);
X va_end (args);
X errno = save_e;
X perror (" ");
X fflush (stderr);
X}
X
X#endif /* HAVE_VPRINTF and not __STDC__ */
X
X#if !defined(HAVE_VPRINTF) && defined(HAVE_DOPRNT)
Xvoid
Xmsg (str, args)
X char *str;
X int args;
X{
X fflush (msg_file);
X fprintf (stderr, "%s: ", tar);
X if (f_sayblock)
X fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
X _doprnt (str, &args, stderr);
X putc ('\n', stderr);
X fflush (stderr);
X}
X
Xvoid
Xmsg_perror (str, args)
X char *str;
X int args;
X{
X int save_e;
X
X save_e = errno;
X fflush (msg_file);
X fprintf (stderr, "%s: ", tar);
X if (f_sayblock)
X fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
X _doprnt (str, &args, stderr);
X errno = save_e;
X perror (" ");
X fflush (stderr);
X}
X
X#endif /* !HAVE_VPRINTF and HAVE_DOPRNT */
X
X#if !defined(HAVE_VPRINTF) && !defined(HAVE_DOPRNT)
Xvoid
Xmsg (str, a1, a2, a3, a4, a5, a6)
X char *str;
X{
X fflush (msg_file);
X fprintf (stderr, "%s: ", tar);
X if (f_sayblock)
X fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
X fprintf (stderr, str, a1, a2, a3, a4, a5, a6);
X putc ('\n', stderr);
X fflush (stderr);
X}
X
Xvoid
Xmsg_perror (str, a1, a2, a3, a4, a5, a6)
X char *str;
X{
X int save_e;
X
X save_e = errno;
X fflush (msg_file);
X fprintf (stderr, "%s: ", tar);
X if (f_sayblock)
X fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
X fprintf (stderr, str, a1, a2, a3, a4, a5, a6);
X fprintf (stderr, ": ");
X errno = save_e;
X perror (" ");
X}
X
X#endif /* !HAVE_VPRINTF and !HAVE_DOPRNT */
END_OF_FILE
if test 25899 -ne `wc -c <'port.c'`; then
echo shar: \"'port.c'\" unpacked with wrong size!
fi
# end of 'port.c'
fi
if test -f 'fnmatch.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'fnmatch.c'\"
else
echo shar: Extracting \"'fnmatch.c'\" \(3960 characters\)
sed "s/^X//" >'fnmatch.c' <<'END_OF_FILE'
X/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
XThis library is free software; you can redistribute it and/or
Xmodify it under the terms of the GNU Library General Public License as
Xpublished by the Free Software Foundation; either version 2 of the
XLicense, or (at your option) any later version.
X
XThis library is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
XLibrary General Public License for more details.
X
XYou should have received a copy of the GNU Library General Public
XLicense along with this library; see the file COPYING.LIB. If
Xnot, write to the Free Software Foundation, Inc., 675 Mass Ave,
XCambridge, MA 02139, USA. */
X
X#include <errno.h>
X#include <fnmatch.h>
X
X#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS)
Xextern int errno;
X#endif
X
X/* Match STRING against the filename pattern PATTERN, returning zero if
X it matches, nonzero if not. */
Xint
Xfnmatch (pattern, string, flags)
X const char *pattern;
X const char *string;
X int flags;
X{
X register const char *p = pattern, *n = string;
X register char c;
X
X if ((flags & ~__FNM_FLAGS) != 0)
X {
X errno = EINVAL;
X return -1;
X }
X
X while ((c = *p++) != '\0')
X {
X switch (c)
X {
X case '?':
X if (*n == '\0')
X return FNM_NOMATCH;
X else if ((flags & FNM_PATHNAME) && *n == '/')
X return FNM_NOMATCH;
X else if ((flags & FNM_PERIOD) && *n == '.' &&
X (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
X return FNM_NOMATCH;
X break;
X
X case '\\':
X if (!(flags & FNM_NOESCAPE))
X c = *p++;
X if (*n != c)
X return FNM_NOMATCH;
X break;
X
X case '*':
X if ((flags & FNM_PERIOD) && *n == '.' &&
X (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
X return FNM_NOMATCH;
X
X for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
X if (((flags & FNM_PATHNAME) && *n == '/') ||
X (c == '?' && *n == '\0'))
X return FNM_NOMATCH;
X
X if (c == '\0')
X return 0;
X
X {
X char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
X for (--p; *n != '\0'; ++n)
X if ((c == '[' || *n == c1) &&
X fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
X return 0;
X return FNM_NOMATCH;
X }
X
X case '[':
X {
X /* Nonzero if the sense of the character class is inverted. */
X register int not;
X
X if (*n == '\0')
X return FNM_NOMATCH;
X
X if ((flags & FNM_PERIOD) && *n == '.' &&
X (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
X return FNM_NOMATCH;
X
X not = (*p == '!' || *p == '^');
X if (not)
X ++p;
X
X c = *p++;
X for (;;)
X {
X register char cstart = c, cend = c;
X
X if (!(flags & FNM_NOESCAPE) && c == '\\')
X cstart = cend = *p++;
X
X if (c == '\0')
X /* [ (unterminated) loses. */
X return FNM_NOMATCH;
X
X c = *p++;
X
X if ((flags & FNM_PATHNAME) && c == '/')
X /* [/] can never match. */
X return FNM_NOMATCH;
X
X if (c == '-' && *p != ']')
X {
X cend = *p++;
X if (!(flags & FNM_NOESCAPE) && cend == '\\')
X cend = *p++;
X if (cend == '\0')
X return FNM_NOMATCH;
X c = *p++;
X }
X
X if (*n >= cstart && *n <= cend)
X goto matched;
X
X if (c == ']')
X break;
X }
X if (!not)
X return FNM_NOMATCH;
X break;
X
X matched:;
X /* Skip the rest of the [...] that already matched. */
X while (c != ']')
X {
X if (c == '\0')
X /* [... (unterminated) loses. */
X return FNM_NOMATCH;
X
X c = *p++;
X if (!(flags & FNM_NOESCAPE) && c == '\\')
X /* 1003.2d11 is unclear if this is right. %%% */
X ++p;
X }
X if (not)
X return FNM_NOMATCH;
X }
X break;
X
X default:
X if (c != *n)
X return FNM_NOMATCH;
X }
X
X ++n;
X }
X
X if (*n == '\0')
X return 0;
X
X if ((flags & FNM_LEADING_DIR) && *n == '/')
X /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
X return 0;
X
X return FNM_NOMATCH;
X}
END_OF_FILE
if test 3960 -ne `wc -c <'fnmatch.c'`; then
echo shar: \"'fnmatch.c'\" unpacked with wrong size!
fi
# end of 'fnmatch.c'
fi
if test -f 'getopt.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'getopt.c'\"
else
echo shar: Extracting \"'getopt.c'\" \(20137 characters\)
sed "s/^X//" >'getopt.c' <<'END_OF_FILE'
X/* Getopt for GNU.
X NOTE: getopt is now part of the C library, so if you don't know what
X "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
X before changing it!
X
X Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
X Free Software Foundation, Inc.
X
X This program is free software; you can redistribute it and/or modify it
X under the terms of the GNU General Public License as published by the
X Free Software Foundation; either version 2, or (at your option) any
X later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* NOTE!!! AIX requires this to be the first thing in the file.
X Do not put ANYTHING before it! */
X#if !defined (__GNUC__) && defined (_AIX)
X #pragma alloca
X#endif
X
X#ifdef HAVE_CONFIG_H
X#include "config.h"
X#endif
X
X#ifdef __GNUC__
X#define alloca __builtin_alloca
X#else /* not __GNUC__ */
X#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__))))
X#include <alloca.h>
X#else
X#ifndef _AIX
Xchar *alloca ();
X#endif
X#endif /* alloca.h */
X#endif /* not __GNUC__ */
X
X#if !__STDC__ && !defined(const) && IN_GCC
X#define const
X#endif
X
X#include <stdio.h>
X
X/* This needs to come after some library #include
X to get __GNU_LIBRARY__ defined. */
X#ifdef __GNU_LIBRARY__
X#undef alloca
X/* Don't include stdlib.h for non-GNU C libraries because some of them
X contain conflicting prototypes for getopt. */
X#include <stdlib.h>
X#else /* Not GNU C library. */
X#define __alloca alloca
X#endif /* GNU C library. */
X
X/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
X long-named option. Because this is not POSIX.2 compliant, it is
X being phased out. */
X/* #define GETOPT_COMPAT */
X
X/* This version of `getopt' appears to the caller like standard Unix `getopt'
X but it behaves differently for the user, since it allows the user
X to intersperse the options with the other arguments.
X
X As `getopt' works, it permutes the elements of ARGV so that,
X when it is done, all the options precede everything else. Thus
X all application programs are extended to handle flexible argument order.
X
X Setting the environment variable POSIXLY_CORRECT disables permutation.
X Then the behavior is completely standard.
X
X GNU application programs can use a third alternative mode in which
X they can distinguish the relative order of options and other arguments. */
X
X#include "getopt.h"
X
X/* For communication from `getopt' to the caller.
X When `getopt' finds an option that takes an argument,
X the argument value is returned here.
X Also, when `ordering' is RETURN_IN_ORDER,
X each non-option ARGV-element is returned here. */
X
Xchar *optarg = 0;
X
X/* Index in ARGV of the next element to be scanned.
X This is used for communication to and from the caller
X and for communication between successive calls to `getopt'.
X
X On entry to `getopt', zero means this is the first call; initialize.
X
X When `getopt' returns EOF, this is the index of the first of the
X non-option elements that the caller should itself scan.
X
X Otherwise, `optind' communicates from one call to the next
X how much of ARGV has been scanned so far. */
X
X/* XXX 1003.2 says this must be 1 before any call. */
Xint optind = 0;
X
X/* The next char to be scanned in the option-element
X in which the last option character we returned was found.
X This allows us to pick up the scan where we left off.
X
X If this is zero, or a null string, it means resume the scan
X by advancing to the next ARGV-element. */
X
Xstatic char *nextchar;
X
X/* Callers store zero here to inhibit the error message
X for unrecognized options. */
X
Xint opterr = 1;
X
X/* Set to an option character which was unrecognized.
X This must be initialized on some systems to avoid linking in the
X system's own getopt implementation. */
X
Xint optopt = '?';
X
X/* Describe how to deal with options that follow non-option ARGV-elements.
X
X If the caller did not specify anything,
X the default is REQUIRE_ORDER if the environment variable
X POSIXLY_CORRECT is defined, PERMUTE otherwise.
X
X REQUIRE_ORDER means don't recognize them as options;
X stop option processing when the first non-option is seen.
X This is what Unix does.
X This mode of operation is selected by either setting the environment
X variable POSIXLY_CORRECT, or using `+' as the first character
X of the list of option characters.
X
X PERMUTE is the default. We permute the contents of ARGV as we scan,
X so that eventually all the non-options are at the end. This allows options
X to be given in any order, even with programs that were not written to
X expect this.
X
X RETURN_IN_ORDER is an option available to programs that were written
X to expect options and other ARGV-elements in any order and that care about
X the ordering of the two. We describe each non-option ARGV-element
X as if it were the argument of an option with character code 1.
X Using `-' as the first character of the list of option characters
X selects this mode of operation.
X
X The special argument `--' forces an end of option-scanning regardless
X of the value of `ordering'. In the case of RETURN_IN_ORDER, only
X `--' can cause `getopt' to return EOF with `optind' != ARGC. */
X
Xstatic enum
X{
X REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
X} ordering;
X
X#ifdef __GNU_LIBRARY__
X/* We want to avoid inclusion of string.h with non-GNU libraries
X because there are many ways it can cause trouble.
X On some systems, it contains special magic macros that don't work
X in GCC. */
X#include <string.h>
X#define my_index strchr
X#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
X#else
X
X/* Avoid depending on library functions or files
X whose names are inconsistent. */
X
Xchar *getenv ();
X
Xstatic char *
Xmy_index (string, chr)
X char *string;
X int chr;
X{
X while (*string)
X {
X if (*string == chr)
X return string;
X string++;
X }
X return 0;
X}
X
Xstatic void
Xmy_bcopy (from, to, size)
X char *from, *to;
X int size;
X{
X int i;
X for (i = 0; i < size; i++)
X to[i] = from[i];
X}
X#endif /* GNU C library. */
X
X/* Handle permutation of arguments. */
X
X/* Describe the part of ARGV that contains non-options that have
X been skipped. `first_nonopt' is the index in ARGV of the first of them;
X `last_nonopt' is the index after the last of them. */
X
Xstatic int first_nonopt;
Xstatic int last_nonopt;
X
X/* Exchange two adjacent subsequences of ARGV.
X One subsequence is elements [first_nonopt,last_nonopt)
X which contains all the non-options that have been skipped so far.
X The other is elements [last_nonopt,optind), which contains all
X the options processed since those non-options were skipped.
X
X `first_nonopt' and `last_nonopt' are relocated so that they describe
X the new indices of the non-options in ARGV after they are moved. */
X
Xstatic void
Xexchange (argv)
X char **argv;
X{
X int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
X char **temp = (char **) __alloca (nonopts_size);
X
X /* Interchange the two blocks of data in ARGV. */
X
X my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size);
X my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt],
X (optind - last_nonopt) * sizeof (char *));
X my_bcopy ((char *) temp,
X (char *) &argv[first_nonopt + optind - last_nonopt],
X nonopts_size);
X
X /* Update records for the slots the non-options now occupy. */
X
X first_nonopt += (optind - last_nonopt);
X last_nonopt = optind;
X}
X
X/* Scan elements of ARGV (whose length is ARGC) for option characters
X given in OPTSTRING.
X
X If an element of ARGV starts with '-', and is not exactly "-" or "--",
X then it is an option element. The characters of this element
X (aside from the initial '-') are option characters. If `getopt'
X is called repeatedly, it returns successively each of the option characters
X from each of the option elements.
X
X If `getopt' finds another option character, it returns that character,
X updating `optind' and `nextchar' so that the next call to `getopt' can
X resume the scan with the following option character or ARGV-element.
X
X If there are no more option characters, `getopt' returns `EOF'.
X Then `optind' is the index in ARGV of the first ARGV-element
X that is not an option. (The ARGV-elements have been permuted
X so that those that are not options now come last.)
X
X OPTSTRING is a string containing the legitimate option characters.
X If an option character is seen that is not listed in OPTSTRING,
X return '?' after printing an error message. If you set `opterr' to
X zero, the error message is suppressed but we still return '?'.
X
X If a char in OPTSTRING is followed by a colon, that means it wants an arg,
X so the following text in the same ARGV-element, or the text of the following
X ARGV-element, is returned in `optarg'. Two colons mean an option that
X wants an optional arg; if there is text in the current ARGV-element,
X it is returned in `optarg', otherwise `optarg' is set to zero.
X
X If OPTSTRING starts with `-' or `+', it requests different methods of
X handling the non-option ARGV-elements.
X See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
X
X Long-named options begin with `--' instead of `-'.
X Their names may be abbreviated as long as the abbreviation is unique
X or is an exact match for some defined option. If they have an
X argument, it follows the option name in the same ARGV-element, separated
X from the option name by a `=', or else the in next ARGV-element.
X When `getopt' finds a long-named option, it returns 0 if that option's
X `flag' field is nonzero, the value of the option's `val' field
X if the `flag' field is zero.
X
X The elements of ARGV aren't really const, because we permute them.
X But we pretend they're const in the prototype to be compatible
X with other systems.
X
X LONGOPTS is a vector of `struct option' terminated by an
X element containing a name which is zero.
X
X LONGIND returns the index in LONGOPT of the long-named option found.
X It is only valid when a long-named option has been found by the most
X recent call.
X
X If LONG_ONLY is nonzero, '-' as well as '--' can introduce
X long-named options. */
X
Xint
X_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
X int argc;
X char *const *argv;
X const char *optstring;
X const struct option *longopts;
X int *longind;
X int long_only;
X{
X int option_index;
X
X optarg = 0;
X
X /* Initialize the internal data when the first call is made.
X Start processing options with ARGV-element 1 (since ARGV-element 0
X is the program name); the sequence of previously skipped
X non-option ARGV-elements is empty. */
X
X if (optind == 0)
X {
X first_nonopt = last_nonopt = optind = 1;
X
X nextchar = NULL;
X
X /* Determine how to handle the ordering of options and nonoptions. */
X
X if (optstring[0] == '-')
X {
X ordering = RETURN_IN_ORDER;
X ++optstring;
X }
X else if (optstring[0] == '+')
X {
X ordering = REQUIRE_ORDER;
X ++optstring;
X }
X else if (getenv ("POSIXLY_CORRECT") != NULL)
X ordering = REQUIRE_ORDER;
X else
X ordering = PERMUTE;
X }
X
X if (nextchar == NULL || *nextchar == '\0')
X {
X if (ordering == PERMUTE)
X {
X /* If we have just processed some options following some non-options,
X exchange them so that the options come first. */
X
X if (first_nonopt != last_nonopt && last_nonopt != optind)
X exchange ((char **) argv);
X else if (last_nonopt != optind)
X first_nonopt = optind;
X
X /* Now skip any additional non-options
X and extend the range of non-options previously skipped. */
X
X while (optind < argc
X && (argv[optind][0] != '-' || argv[optind][1] == '\0')
X#ifdef GETOPT_COMPAT
X && (longopts == NULL
X || argv[optind][0] != '+' || argv[optind][1] == '\0')
X#endif /* GETOPT_COMPAT */
X )
X optind++;
X last_nonopt = optind;
X }
X
X /* Special ARGV-element `--' means premature end of options.
X Skip it like a null option,
X then exchange with previous non-options as if it were an option,
X then skip everything else like a non-option. */
X
X if (optind != argc && !strcmp (argv[optind], "--"))
X {
X optind++;
X
X if (first_nonopt != last_nonopt && last_nonopt != optind)
X exchange ((char **) argv);
X else if (first_nonopt == last_nonopt)
X first_nonopt = optind;
X last_nonopt = argc;
X
X optind = argc;
X }
X
X /* If we have done all the ARGV-elements, stop the scan
X and back over any non-options that we skipped and permuted. */
X
X if (optind == argc)
X {
X /* Set the next-arg-index to point at the non-options
X that we previously skipped, so the caller will digest them. */
X if (first_nonopt != last_nonopt)
X optind = first_nonopt;
X return EOF;
X }
X
X /* If we have come to a non-option and did not permute it,
X either stop the scan or describe it to the caller and pass it by. */
X
X if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
X#ifdef GETOPT_COMPAT
X && (longopts == NULL
X || argv[optind][0] != '+' || argv[optind][1] == '\0')
X#endif /* GETOPT_COMPAT */
X )
X {
X if (ordering == REQUIRE_ORDER)
X return EOF;
X optarg = argv[optind++];
X return 1;
X }
X
X /* We have found another option-ARGV-element.
X Start decoding its characters. */
X
X nextchar = (argv[optind] + 1
X + (longopts != NULL && argv[optind][1] == '-'));
X }
X
X if (longopts != NULL
X && ((argv[optind][0] == '-'
X && (argv[optind][1] == '-' || long_only))
X#ifdef GETOPT_COMPAT
X || argv[optind][0] == '+'
X#endif /* GETOPT_COMPAT */
X ))
X {
X const struct option *p;
X char *s = nextchar;
X int exact = 0;
X int ambig = 0;
X const struct option *pfound = NULL;
X int indfound;
X
X while (*s && *s != '=')
X s++;
X
X /* Test all options for either exact match or abbreviated matches. */
X for (p = longopts, option_index = 0; p->name;
X p++, option_index++)
X if (!strncmp (p->name, nextchar, s - nextchar))
X {
X if (s - nextchar == strlen (p->name))
X {
X /* Exact match found. */
X pfound = p;
X indfound = option_index;
X exact = 1;
X break;
X }
X else if (pfound == NULL)
X {
X /* First nonexact match found. */
X pfound = p;
X indfound = option_index;
X }
X else
X /* Second nonexact match found. */
X ambig = 1;
X }
X
X if (ambig && !exact)
X {
X if (opterr)
X fprintf (stderr, "%s: option `%s' is ambiguous\n",
X argv[0], argv[optind]);
X nextchar += strlen (nextchar);
X optind++;
X return '?';
X }
X
X if (pfound != NULL)
X {
X option_index = indfound;
X optind++;
X if (*s)
X {
X /* Don't test has_arg with >, because some C compilers don't
X allow it to be used on enums. */
X if (pfound->has_arg)
X optarg = s + 1;
X else
X {
X if (opterr)
X {
X if (argv[optind - 1][1] == '-')
X /* --option */
X fprintf (stderr,
X "%s: option `--%s' doesn't allow an argument\n",
X argv[0], pfound->name);
X else
X /* +option or -option */
X fprintf (stderr,
X "%s: option `%c%s' doesn't allow an argument\n",
X argv[0], argv[optind - 1][0], pfound->name);
X }
X nextchar += strlen (nextchar);
X return '?';
X }
X }
X else if (pfound->has_arg == 1)
X {
X if (optind < argc)
X optarg = argv[optind++];
X else
X {
X if (opterr)
X fprintf (stderr, "%s: option `%s' requires an argument\n",
X argv[0], argv[optind - 1]);
X nextchar += strlen (nextchar);
X return optstring[0] == ':' ? ':' : '?';
X }
X }
X nextchar += strlen (nextchar);
X if (longind != NULL)
X *longind = option_index;
X if (pfound->flag)
X {
X *(pfound->flag) = pfound->val;
X return 0;
X }
X return pfound->val;
X }
X /* Can't find it as a long option. If this is not getopt_long_only,
X or the option starts with '--' or is not a valid short
X option, then it's an error.
X Otherwise interpret it as a short option. */
X if (!long_only || argv[optind][1] == '-'
X#ifdef GETOPT_COMPAT
X || argv[optind][0] == '+'
X#endif /* GETOPT_COMPAT */
X || my_index (optstring, *nextchar) == NULL)
X {
X if (opterr)
X {
X if (argv[optind][1] == '-')
X /* --option */
X fprintf (stderr, "%s: unrecognized option `--%s'\n",
X argv[0], nextchar);
X else
X /* +option or -option */
X fprintf (stderr, "%s: unrecognized option `%c%s'\n",
X argv[0], argv[optind][0], nextchar);
X }
X nextchar = (char *) "";
X optind++;
X return '?';
X }
X }
X
X /* Look at and handle the next option-character. */
X
X {
X char c = *nextchar++;
X char *temp = my_index (optstring, c);
X
X /* Increment `optind' when we start to process its last character. */
X if (*nextchar == '\0')
X ++optind;
X
X if (temp == NULL || c == ':')
X {
X if (opterr)
X {
X#if 0
X if (c < 040 || c >= 0177)
X fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
X argv[0], c);
X else
X fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
X#else
X /* 1003.2 specifies the format of this message. */
X fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
X#endif
X }
X optopt = c;
X return '?';
X }
X if (temp[1] == ':')
X {
X if (temp[2] == ':')
X {
X /* This is an option that accepts an argument optionally. */
X if (*nextchar != '\0')
X {
X optarg = nextchar;
X optind++;
X }
X else
X optarg = 0;
X nextchar = NULL;
X }
X else
X {
X /* This is an option that requires an argument. */
X if (*nextchar != '\0')
X {
X optarg = nextchar;
X /* If we end this ARGV-element by taking the rest as an arg,
X we must advance to the next element now. */
X optind++;
X }
X else if (optind == argc)
X {
X if (opterr)
X {
X#if 0
X fprintf (stderr, "%s: option `-%c' requires an argument\n",
X argv[0], c);
X#else
X /* 1003.2 specifies the format of this message. */
X fprintf (stderr, "%s: option requires an argument -- %c\n",
X argv[0], c);
X#endif
X }
X optopt = c;
X if (optstring[0] == ':')
X c = ':';
X else
X c = '?';
X }
X else
X /* We already incremented `optind' once;
X increment it again when taking next ARGV-elt as argument. */
X optarg = argv[optind++];
X nextchar = NULL;
X }
X }
X return c;
X }
X}
X
Xint
Xgetopt (argc, argv, optstring)
X int argc;
X char *const *argv;
X const char *optstring;
X{
X return _getopt_internal (argc, argv, optstring,
X (const struct option *) 0,
X (int *) 0,
X 0);
X}
X
X#ifdef TEST
X
X/* Compile with -DTEST to make an executable for use in testing
X the above definition of `getopt'. */
X
Xint
Xmain (argc, argv)
X int argc;
X char **argv;
X{
X int c;
X int digit_optind = 0;
X
X while (1)
X {
X int this_option_optind = optind ? optind : 1;
X
X c = getopt (argc, argv, "abc:d:0123456789");
X if (c == EOF)
X break;
X
X switch (c)
X {
X case '0':
X case '1':
X case '2':
X case '3':
X case '4':
X case '5':
X case '6':
X case '7':
X case '8':
X case '9':
X if (digit_optind != 0 && digit_optind != this_option_optind)
X printf ("digits occur in two different argv-elements.\n");
X digit_optind = this_option_optind;
X printf ("option %c\n", c);
X break;
X
X case 'a':
X printf ("option a\n");
X break;
X
X case 'b':
X printf ("option b\n");
X break;
X
X case 'c':
X printf ("option c with value `%s'\n", optarg);
X break;
X
X case '?':
X break;
X
X default:
X printf ("?? getopt returned character code 0%o ??\n", c);
X }
X }
X
X if (optind < argc)
X {
X printf ("non-option ARGV-elements: ");
X while (optind < argc)
X printf ("%s ", argv[optind++]);
X printf ("\n");
X }
X
X exit (0);
X}
X
X#endif /* TEST */
END_OF_FILE
if test 20137 -ne `wc -c <'getopt.c'`; then
echo shar: \"'getopt.c'\" unpacked with wrong size!
fi
# end of 'getopt.c'
fi
if test -f 'malloc.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'malloc.c'\"
else
echo shar: Extracting \"'malloc.c'\" \(30710 characters\)
sed "s/^X//" >'malloc.c' <<'END_OF_FILE'
X/* DO NOT EDIT THIS FILE -- it is automagically generated. -*- C -*- */
X
X#define _MALLOC_INTERNAL
X
X/* The malloc headers and source files from the C library follow here. */
X
X/* Declarations for `malloc' and friends.
X Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
X Written May 1989 by Mike Haertel.
X
XThis library is free software; you can redistribute it and/or
Xmodify it under the terms of the GNU Library General Public License as
Xpublished by the Free Software Foundation; either version 2 of the
XLicense, or (at your option) any later version.
X
XThis library is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
XLibrary General Public License for more details.
X
XYou should have received a copy of the GNU Library General Public
XLicense along with this library; see the file COPYING.LIB. If
Xnot, write to the Free Software Foundation, Inc., 675 Mass Ave,
XCambridge, MA 02139, USA.
X
X The author may be reached (Email) at the address mike@ai.mit.edu,
X or (US mail) as Mike Haertel c/o Free Software Foundation. */
X
X#ifndef _MALLOC_H
X
X#define _MALLOC_H 1
X
X#ifdef _MALLOC_INTERNAL
X/* Harmless, gets __GNU_LIBRARY__ defined.
X We must do this before #defining size_t and ptrdiff_t
X because <stdio.h> tries to typedef them on some systems. */
X#include <stdio.h>
X#endif
X
X#ifdef __cplusplus
Xextern "C"
X{
X#endif
X
X#if defined (__cplusplus) || (defined (__STDC__) && __STDC__)
X#undef __P
X#define __P(args) args
X#undef __ptr_t
X#define __ptr_t void *
X#else /* Not C++ or ANSI C. */
X#undef __P
X#define __P(args) ()
X#undef const
X#define const
X#undef __ptr_t
X#define __ptr_t char *
X#endif /* C++ or ANSI C. */
X
X#ifndef NULL
X#define NULL 0
X#endif
X
X#ifdef __STDC__
X#include <stddef.h>
X#else
X#undef size_t
X#define size_t unsigned int
X#undef ptrdiff_t
X#define ptrdiff_t int
X#endif
X
X
X/* Allocate SIZE bytes of memory. */
Xextern __ptr_t malloc __P ((size_t __size));
X/* Re-allocate the previously allocated block
X in __ptr_t, making the new block SIZE bytes long. */
Xextern __ptr_t realloc __P ((__ptr_t __ptr, size_t __size));
X/* Allocate NMEMB elements of SIZE bytes each, all initialized to 0. */
Xextern __ptr_t calloc __P ((size_t __nmemb, size_t __size));
X/* Free a block allocated by `malloc', `realloc' or `calloc'. */
Xextern void free __P ((__ptr_t __ptr));
X
X/* Allocate SIZE bytes allocated to ALIGNMENT bytes. */
Xextern __ptr_t memalign __P ((size_t __alignment, size_t __size));
X
X/* Allocate SIZE bytes on a page boundary. */
Xextern __ptr_t valloc __P ((size_t __size));
X
X
X#ifdef _MALLOC_INTERNAL
X
X#ifdef HAVE_CONFIG_H
X#include "config.h"
X#endif
X
X#if defined(__GNU_LIBRARY__) || defined(STDC_HEADERS) || defined(USG)
X#include <string.h>
X#else
X#ifndef memset
X#define memset(s, zero, n) bzero ((s), (n))
X#endif
X#ifndef memcpy
X#define memcpy(d, s, n) bcopy ((s), (d), (n))
X#endif
X#ifndef memmove
X#define memmove(d, s, n) bcopy ((s), (d), (n))
X#endif
X#endif
X
X
X#if defined(__GNU_LIBRARY__) || defined(__STDC__)
X#include <limits.h>
X#else
X#define CHAR_BIT 8
X#endif
X
X/* The allocator divides the heap into blocks of fixed size; large
X requests receive one or more whole blocks, and small requests
X receive a fragment of a block. Fragment sizes are powers of two,
X and all fragments of a block are the same size. When all the
X fragments in a block have been freed, the block itself is freed. */
X#define INT_BIT (CHAR_BIT * sizeof(int))
X#define BLOCKLOG (INT_BIT > 16 ? 12 : 9)
X#define BLOCKSIZE (1 << BLOCKLOG)
X#define BLOCKIFY(SIZE) (((SIZE) + BLOCKSIZE - 1) / BLOCKSIZE)
X
X/* Determine the amount of memory spanned by the initial heap table
X (not an absolute limit). */
X#define HEAP (INT_BIT > 16 ? 4194304 : 65536)
X
X/* Number of contiguous free blocks allowed to build up at the end of
X memory before they will be returned to the system. */
X#define FINAL_FREE_BLOCKS 8
X
X/* Where to start searching the free list when looking for new memory.
X The two possible values are 0 and _heapindex. Starting at 0 seems
X to reduce total memory usage, while starting at _heapindex seems to
X run faster. */
X#define MALLOC_SEARCH_START _heapindex
X
X/* Data structure giving per-block information. */
Xtypedef union
X {
X /* Heap information for a busy block. */
X struct
X {
X /* Zero for a large block, or positive giving the
X logarithm to the base two of the fragment size. */
X int type;
X union
X {
X struct
X {
X size_t nfree; /* Free fragments in a fragmented block. */
X size_t first; /* First free fragment of the block. */
X } frag;
X /* Size (in blocks) of a large cluster. */
X size_t size;
X } info;
X } busy;
X /* Heap information for a free block
X (that may be the first of a free cluster). */
X struct
X {
X size_t size; /* Size (in blocks) of a free cluster. */
X size_t next; /* Index of next free cluster. */
X size_t prev; /* Index of previous free cluster. */
X } free;
X } malloc_info;
X
X/* Pointer to first block of the heap. */
Xextern char *_heapbase;
X
X/* Table indexed by block number giving per-block information. */
Xextern malloc_info *_heapinfo;
X
X/* Address to block number and vice versa. */
X#define BLOCK(A) (((char *) (A) - _heapbase) / BLOCKSIZE + 1)
X#define ADDRESS(B) ((__ptr_t) (((B) - 1) * BLOCKSIZE + _heapbase))
X
X/* Current search index for the heap table. */
Xextern size_t _heapindex;
X
X/* Limit of valid info table indices. */
Xextern size_t _heaplimit;
X
X/* Doubly linked lists of free fragments. */
Xstruct list
X {
X struct list *next;
X struct list *prev;
X };
X
X/* Free list headers for each fragment size. */
Xextern struct list _fraghead[];
X
X/* List of blocks allocated with `memalign' (or `valloc'). */
Xstruct alignlist
X {
X struct alignlist *next;
X __ptr_t aligned; /* The address that memaligned returned. */
X __ptr_t exact; /* The address that malloc returned. */
X };
Xextern struct alignlist *_aligned_blocks;
X
X/* Instrumentation. */
Xextern size_t _chunks_used;
Xextern size_t _bytes_used;
Xextern size_t _chunks_free;
Xextern size_t _bytes_free;
X
X/* Internal version of `free' used in `morecore' (malloc.c). */
Xextern void _free_internal __P ((__ptr_t __ptr));
X
X#endif /* _MALLOC_INTERNAL. */
X
X/* Underlying allocation function; successive calls should
X return contiguous pieces of memory. */
Xextern __ptr_t (*__morecore) __P ((ptrdiff_t __size));
X
X/* Default value of `__morecore'. */
Xextern __ptr_t __default_morecore __P ((ptrdiff_t __size));
X
X/* If not NULL, this function is called after each time
X `__morecore' is called to increase the data size. */
Xextern void (*__after_morecore_hook) __P ((void));
X
X/* Nonzero if `malloc' has been called and done its initialization. */
Xextern int __malloc_initialized;
X
X/* Hooks for debugging versions. */
Xextern void (*__free_hook) __P ((__ptr_t __ptr));
Xextern __ptr_t (*__malloc_hook) __P ((size_t __size));
Xextern __ptr_t (*__realloc_hook) __P ((__ptr_t __ptr, size_t __size));
X
X/* Activate a standard collection of debugging hooks. */
Xextern int mcheck __P ((void (*__func) __P ((void))));
X
X/* Activate a standard collection of tracing hooks. */
Xextern void mtrace __P ((void));
X
X/* Statistics available to the user. */
Xstruct mstats
X {
X size_t bytes_total; /* Total size of the heap. */
X size_t chunks_used; /* Chunks allocated by the user. */
X size_t bytes_used; /* Byte total of user-allocated chunks. */
X size_t chunks_free; /* Chunks in the free list. */
X size_t bytes_free; /* Byte total of chunks in the free list. */
X };
X
X/* Pick up the current statistics. */
Xextern struct mstats mstats __P ((void));
X
X/* Call WARNFUN with a warning message when memory usage is high. */
Xextern void memory_warnings __P ((__ptr_t __start,
X void (*__warnfun) __P ((__const char *))));
X
X
X/* Relocating allocator. */
X
X/* Allocate SIZE bytes, and store the address in *HANDLEPTR. */
Xextern __ptr_t r_alloc __P ((__ptr_t *__handleptr, size_t __size));
X
X/* Free the storage allocated in HANDLEPTR. */
Xextern void r_alloc_free __P ((__ptr_t *__handleptr));
X
X/* Adjust the block at HANDLEPTR to be SIZE bytes long. */
Xextern __ptr_t r_re_alloc __P ((__ptr_t *__handleptr, size_t __size));
X
X
X#ifdef __cplusplus
X}
X#endif
X
X#endif /* malloc.h */
X/* Memory allocator `malloc'.
X Copyright 1990, 1991, 1992 Free Software Foundation
X Written May 1989 by Mike Haertel.
X
XThis library is free software; you can redistribute it and/or
Xmodify it under the terms of the GNU Library General Public License as
Xpublished by the Free Software Foundation; either version 2 of the
XLicense, or (at your option) any later version.
X
XThis library is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
XLibrary General Public License for more details.
X
XYou should have received a copy of the GNU Library General Public
XLicense along with this library; see the file COPYING.LIB. If
Xnot, write to the Free Software Foundation, Inc., 675 Mass Ave,
XCambridge, MA 02139, USA.
X
X The author may be reached (Email) at the address mike@ai.mit.edu,
X or (US mail) as Mike Haertel c/o Free Software Foundation. */
X
X#ifndef _MALLOC_INTERNAL
X#define _MALLOC_INTERNAL
X#include <malloc.h>
X#endif
X
X/* How to really get more memory. */
X__ptr_t (*__morecore) __P ((ptrdiff_t __size)) = __default_morecore;
X
X/* Debugging hook for `malloc'. */
X__ptr_t (*__malloc_hook) __P ((size_t __size));
X
X/* Pointer to the base of the first block. */
Xchar *_heapbase;
X
X/* Block information table. Allocated with align/__free (not malloc/free). */
Xmalloc_info *_heapinfo;
X
X/* Number of info entries. */
Xstatic size_t heapsize;
X
X/* Search index in the info table. */
Xsize_t _heapindex;
X
X/* Limit of valid info table indices. */
Xsize_t _heaplimit;
X
X/* Free lists for each fragment size. */
Xstruct list _fraghead[BLOCKLOG];
X
X/* Instrumentation. */
Xsize_t _chunks_used;
Xsize_t _bytes_used;
Xsize_t _chunks_free;
Xsize_t _bytes_free;
X
X/* Are you experienced? */
Xint __malloc_initialized;
X
Xvoid (*__after_morecore_hook) __P ((void));
X
X/* Aligned allocation. */
Xstatic __ptr_t align __P ((size_t));
Xstatic __ptr_t
Xalign (size)
X size_t size;
X{
X __ptr_t result;
X unsigned long int adj;
X
X result = (*__morecore) (size);
X adj = (unsigned long int) ((unsigned long int) ((char *) result -
X (char *) NULL)) % BLOCKSIZE;
X if (adj != 0)
X {
X adj = BLOCKSIZE - adj;
X (void) (*__morecore) (adj);
X result = (char *) result + adj;
X }
X
X if (__after_morecore_hook)
X (*__after_morecore_hook) ();
X
X return result;
X}
X
X/* Set everything up and remember that we have. */
Xstatic int initialize __P ((void));
Xstatic int
Xinitialize ()
X{
X heapsize = HEAP / BLOCKSIZE;
X _heapinfo = (malloc_info *) align (heapsize * sizeof (malloc_info));
X if (_heapinfo == NULL)
X return 0;
X memset (_heapinfo, 0, heapsize * sizeof (malloc_info));
X _heapinfo[0].free.size = 0;
X _heapinfo[0].free.next = _heapinfo[0].free.prev = 0;
X _heapindex = 0;
X _heapbase = (char *) _heapinfo;
X __malloc_initialized = 1;
X return 1;
X}
X
X/* Get neatly aligned memory, initializing or
X growing the heap info table as necessary. */
Xstatic __ptr_t morecore __P ((size_t));
Xstatic __ptr_t
Xmorecore (size)
X size_t size;
X{
X __ptr_t result;
X malloc_info *newinfo, *oldinfo;
X size_t newsize;
X
X result = align (size);
X if (result == NULL)
X return NULL;
X
X /* Check if we need to grow the info table. */
X if ((size_t) BLOCK ((char *) result + size) > heapsize)
X {
X newsize = heapsize;
X while ((size_t) BLOCK ((char *) result + size) > newsize)
X newsize *= 2;
X newinfo = (malloc_info *) align (newsize * sizeof (malloc_info));
X if (newinfo == NULL)
X {
X (*__morecore) (-size);
X return NULL;
X }
X memset (newinfo, 0, newsize * sizeof (malloc_info));
X memcpy (newinfo, _heapinfo, heapsize * sizeof (malloc_info));
X oldinfo = _heapinfo;
X newinfo[BLOCK (oldinfo)].busy.type = 0;
X newinfo[BLOCK (oldinfo)].busy.info.size
X = BLOCKIFY (heapsize * sizeof (malloc_info));
X _heapinfo = newinfo;
X _free_internal (oldinfo);
X heapsize = newsize;
X }
X
X _heaplimit = BLOCK ((char *) result + size);
X return result;
X}
X
X/* Allocate memory from the heap. */
X__ptr_t
Xmalloc (size)
X size_t size;
X{
X __ptr_t result;
X size_t block, blocks, lastblocks, start;
X register size_t i;
X struct list *next;
X
X if (size == 0)
X return NULL;
X
X if (__malloc_hook != NULL)
X return (*__malloc_hook) (size);
X
X if (!__malloc_initialized)
X if (!initialize ())
X return NULL;
X
X if (size < sizeof (struct list))
X size = sizeof (struct list);
X
X /* Determine the allocation policy based on the request size. */
X if (size <= BLOCKSIZE / 2)
X {
X /* Small allocation to receive a fragment of a block.
X Determine the logarithm to base two of the fragment size. */
X register size_t log = 1;
X --size;
X while ((size /= 2) != 0)
X ++log;
X
X /* Look in the fragment lists for a
X free fragment of the desired size. */
X next = _fraghead[log].next;
X if (next != NULL)
X {
X /* There are free fragments of this size.
X Pop a fragment out of the fragment list and return it.
X Update the block's nfree and first counters. */
X result = (__ptr_t) next;
X next->prev->next = next->next;
X if (next->next != NULL)
X next->next->prev = next->prev;
X block = BLOCK (result);
X if (--_heapinfo[block].busy.info.frag.nfree != 0)
X _heapinfo[block].busy.info.frag.first = (unsigned long int)
X ((unsigned long int) ((char *) next->next - (char *) NULL)
X % BLOCKSIZE) >> log;
X
X /* Update the statistics. */
X ++_chunks_used;
X _bytes_used += 1 << log;
X --_chunks_free;
X _bytes_free -= 1 << log;
X }
X else
X {
X /* No free fragments of the desired size, so get a new block
X and break it into fragments, returning the first. */
X result = malloc (BLOCKSIZE);
X if (result == NULL)
X return NULL;
X
X /* Link all fragments but the first into the free list. */
X for (i = 1; i < (size_t) (BLOCKSIZE >> log); ++i)
X {
X next = (struct list *) ((char *) result + (i << log));
X next->next = _fraghead[log].next;
X next->prev = &_fraghead[log];
X next->prev->next = next;
X if (next->next != NULL)
X next->next->prev = next;
X }
X
X /* Initialize the nfree and first counters for this block. */
X block = BLOCK (result);
X _heapinfo[block].busy.type = log;
X _heapinfo[block].busy.info.frag.nfree = i - 1;
X _heapinfo[block].busy.info.frag.first = i - 1;
X
X _chunks_free += (BLOCKSIZE >> log) - 1;
X _bytes_free += BLOCKSIZE - (1 << log);
X _bytes_used -= BLOCKSIZE - (1 << log);
X }
X }
X else
X {
X /* Large allocation to receive one or more blocks.
X Search the free list in a circle starting at the last place visited.
X If we loop completely around without finding a large enough
X space we will have to get more memory from the system. */
X blocks = BLOCKIFY (size);
X start = block = MALLOC_SEARCH_START;
X while (_heapinfo[block].free.size < blocks)
X {
X block = _heapinfo[block].free.next;
X if (block == start)
X {
X /* Need to get more from the system. Check to see if
X the new core will be contiguous with the final free
X block; if so we don't need to get as much. */
X block = _heapinfo[0].free.prev;
X lastblocks = _heapinfo[block].free.size;
X if (_heaplimit != 0 && block + lastblocks == _heaplimit &&
X (*__morecore) (0) == ADDRESS (block + lastblocks) &&
X (morecore ((blocks - lastblocks) * BLOCKSIZE)) != NULL)
X {
X _heapinfo[block].free.size = blocks;
X _bytes_free += (blocks - lastblocks) * BLOCKSIZE;
X continue;
X }
X result = morecore (blocks * BLOCKSIZE);
X if (result == NULL)
X return NULL;
X block = BLOCK (result);
X _heapinfo[block].busy.type = 0;
X _heapinfo[block].busy.info.size = blocks;
X ++_chunks_used;
X _bytes_used += blocks * BLOCKSIZE;
X return result;
X }
X }
X
X /* At this point we have found a suitable free list entry.
X Figure out how to remove what we need from the list. */
X result = ADDRESS (block);
X if (_heapinfo[block].free.size > blocks)
X {
X /* The block we found has a bit left over,
X so relink the tail end back into the free list. */
X _heapinfo[block + blocks].free.size
X = _heapinfo[block].free.size - blocks;
X _heapinfo[block + blocks].free.next
X = _heapinfo[block].free.next;
X _heapinfo[block + blocks].free.prev
X = _heapinfo[block].free.prev;
X _heapinfo[_heapinfo[block].free.prev].free.next
X = _heapinfo[_heapinfo[block].free.next].free.prev
X = _heapindex = block + blocks;
X }
X else
X {
X /* The block exactly matches our requirements,
X so just remove it from the list. */
X _heapinfo[_heapinfo[block].free.next].free.prev
X = _heapinfo[block].free.prev;
X _heapinfo[_heapinfo[block].free.prev].free.next
X = _heapindex = _heapinfo[block].free.next;
X --_chunks_free;
X }
X
X _heapinfo[block].busy.type = 0;
X _heapinfo[block].busy.info.size = blocks;
X ++_chunks_used;
X _bytes_used += blocks * BLOCKSIZE;
X _bytes_free -= blocks * BLOCKSIZE;
X }
X
X return result;
X}
X/* Free a block of memory allocated by `malloc'.
X Copyright 1990, 1991, 1992 Free Software Foundation
X Written May 1989 by Mike Haertel.
X
XThis library is free software; you can redistribute it and/or
Xmodify it under the terms of the GNU Library General Public License as
Xpublished by the Free Software Foundation; either version 2 of the
XLicense, or (at your option) any later version.
X
XThis library is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
XLibrary General Public License for more details.
X
XYou should have received a copy of the GNU Library General Public
XLicense along with this library; see the file COPYING.LIB. If
Xnot, write to the Free Software Foundation, Inc., 675 Mass Ave,
XCambridge, MA 02139, USA.
X
X The author may be reached (Email) at the address mike@ai.mit.edu,
X or (US mail) as Mike Haertel c/o Free Software Foundation. */
X
X#ifndef _MALLOC_INTERNAL
X#define _MALLOC_INTERNAL
X#include <malloc.h>
X#endif
X
X/* Debugging hook for free. */
Xvoid (*__free_hook) __P ((__ptr_t __ptr));
X
X/* List of blocks allocated by memalign. */
Xstruct alignlist *_aligned_blocks = NULL;
X
X/* Return memory to the heap.
X Like `free' but don't call a __free_hook if there is one. */
Xvoid
X_free_internal (ptr)
X __ptr_t ptr;
X{
X int type;
X size_t block, blocks;
X register size_t i;
X struct list *prev, *next;
X
X block = BLOCK (ptr);
X
X type = _heapinfo[block].busy.type;
X switch (type)
X {
X case 0:
X /* Get as many statistics as early as we can. */
X --_chunks_used;
X _bytes_used -= _heapinfo[block].busy.info.size * BLOCKSIZE;
X _bytes_free += _heapinfo[block].busy.info.size * BLOCKSIZE;
X
X /* Find the free cluster previous to this one in the free list.
X Start searching at the last block referenced; this may benefit
X programs with locality of allocation. */
X i = _heapindex;
X if (i > block)
X while (i > block)
X i = _heapinfo[i].free.prev;
X else
X {
X do
X i = _heapinfo[i].free.next;
X while (i > 0 && i < block);
X i = _heapinfo[i].free.prev;
X }
X
X /* Determine how to link this block into the free list. */
X if (block == i + _heapinfo[i].free.size)
X {
X /* Coalesce this block with its predecessor. */
X _heapinfo[i].free.size += _heapinfo[block].busy.info.size;
X block = i;
X }
X else
X {
X /* Really link this block back into the free list. */
X _heapinfo[block].free.size = _heapinfo[block].busy.info.size;
X _heapinfo[block].free.next = _heapinfo[i].free.next;
X _heapinfo[block].free.prev = i;
X _heapinfo[i].free.next = block;
X _heapinfo[_heapinfo[block].free.next].free.prev = block;
X ++_chunks_free;
X }
X
X /* Now that the block is linked in, see if we can coalesce it
X with its successor (by deleting its successor from the list
X and adding in its size). */
X if (block + _heapinfo[block].free.size == _heapinfo[block].free.next)
X {
X _heapinfo[block].free.size
X += _heapinfo[_heapinfo[block].free.next].free.size;
X _heapinfo[block].free.next
X = _heapinfo[_heapinfo[block].free.next].free.next;
X _heapinfo[_heapinfo[block].free.next].free.prev = block;
X --_chunks_free;
X }
X
X /* Now see if we can return stuff to the system. */
X blocks = _heapinfo[block].free.size;
X if (blocks >= FINAL_FREE_BLOCKS && block + blocks == _heaplimit
X && (*__morecore) (0) == ADDRESS (block + blocks))
X {
X register size_t bytes = blocks * BLOCKSIZE;
X _heaplimit -= blocks;
X (*__morecore) (-bytes);
X _heapinfo[_heapinfo[block].free.prev].free.next
X = _heapinfo[block].free.next;
X _heapinfo[_heapinfo[block].free.next].free.prev
X = _heapinfo[block].free.prev;
X block = _heapinfo[block].free.prev;
X --_chunks_free;
X _bytes_free -= bytes;
X }
X
X /* Set the next search to begin at this block. */
X _heapindex = block;
X break;
X
X default:
X /* Do some of the statistics. */
X --_chunks_used;
X _bytes_used -= 1 << type;
X ++_chunks_free;
X _bytes_free += 1 << type;
X
X /* Get the address of the first free fragment in this block. */
X prev = (struct list *) ((char *) ADDRESS (block) +
X (_heapinfo[block].busy.info.frag.first << type));
X
X if (_heapinfo[block].busy.info.frag.nfree == (BLOCKSIZE >> type) - 1)
X {
X /* If all fragments of this block are free, remove them
X from the fragment list and free the whole block. */
X next = prev;
X for (i = 1; i < (size_t) (BLOCKSIZE >> type); ++i)
X next = next->next;
X prev->prev->next = next;
X if (next != NULL)
X next->prev = prev->prev;
X _heapinfo[block].busy.type = 0;
X _heapinfo[block].busy.info.size = 1;
X
X /* Keep the statistics accurate. */
X ++_chunks_used;
X _bytes_used += BLOCKSIZE;
X _chunks_free -= BLOCKSIZE >> type;
X _bytes_free -= BLOCKSIZE;
X
X free (ADDRESS (block));
X }
X else if (_heapinfo[block].busy.info.frag.nfree != 0)
X {
X /* If some fragments of this block are free, link this
X fragment into the fragment list after the first free
X fragment of this block. */
X next = (struct list *) ptr;
X next->next = prev->next;
X next->prev = prev;
X prev->next = next;
X if (next->next != NULL)
X next->next->prev = next;
X ++_heapinfo[block].busy.info.frag.nfree;
X }
X else
X {
X /* No fragments of this block are free, so link this
X fragment into the fragment list and announce that
X it is the first free fragment of this block. */
X prev = (struct list *) ptr;
X _heapinfo[block].busy.info.frag.nfree = 1;
X _heapinfo[block].busy.info.frag.first = (unsigned long int)
X ((unsigned long int) ((char *) ptr - (char *) NULL)
X % BLOCKSIZE >> type);
X prev->next = _fraghead[type].next;
X prev->prev = &_fraghead[type];
X prev->prev->next = prev;
X if (prev->next != NULL)
X prev->next->prev = prev;
X }
X break;
X }
X}
X
X/* Return memory to the heap. */
Xvoid
Xfree (ptr)
X __ptr_t ptr;
X{
X register struct alignlist *l;
X
X if (ptr == NULL)
X return;
X
X for (l = _aligned_blocks; l != NULL; l = l->next)
X if (l->aligned == ptr)
X {
X l->aligned = NULL; /* Mark the slot in the list as free. */
X ptr = l->exact;
X break;
X }
X
X if (__free_hook != NULL)
X (*__free_hook) (ptr);
X else
X _free_internal (ptr);
X}
X/* Change the size of a block allocated by `malloc'.
X Copyright 1990, 1991, 1992 Free Software Foundation, Inc.
X Written May 1989 by Mike Haertel.
X
XThis library is free software; you can redistribute it and/or
Xmodify it under the terms of the GNU Library General Public License as
Xpublished by the Free Software Foundation; either version 2 of the
XLicense, or (at your option) any later version.
X
XThis library is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
XLibrary General Public License for more details.
X
XYou should have received a copy of the GNU Library General Public
XLicense along with this library; see the file COPYING.LIB. If
Xnot, write to the Free Software Foundation, Inc., 675 Mass Ave,
XCambridge, MA 02139, USA.
X
X The author may be reached (Email) at the address mike@ai.mit.edu,
X or (US mail) as Mike Haertel c/o Free Software Foundation. */
X
X#ifndef _MALLOC_INTERNAL
X#define _MALLOC_INTERNAL
X#include <malloc.h>
X#endif
X
X#define min(A, B) ((A) < (B) ? (A) : (B))
X
X/* Debugging hook for realloc. */
X__ptr_t (*__realloc_hook) __P ((__ptr_t __ptr, size_t __size));
X
X/* Resize the given region to the new size, returning a pointer
X to the (possibly moved) region. This is optimized for speed;
X some benchmarks seem to indicate that greater compactness is
X achieved by unconditionally allocating and copying to a
X new region. This module has incestuous knowledge of the
X internals of both free and malloc. */
X__ptr_t
Xrealloc (ptr, size)
X __ptr_t ptr;
X size_t size;
X{
X __ptr_t result;
X int type;
X size_t block, blocks, oldlimit;
X
X if (size == 0)
X {
X free (ptr);
X return malloc (0);
X }
X else if (ptr == NULL)
X return malloc (size);
X
X if (__realloc_hook != NULL)
X return (*__realloc_hook) (ptr, size);
X
X block = BLOCK (ptr);
X
X type = _heapinfo[block].busy.type;
X switch (type)
X {
X case 0:
X /* Maybe reallocate a large block to a small fragment. */
X if (size <= BLOCKSIZE / 2)
X {
X result = malloc (size);
X if (result != NULL)
X {
X memcpy (result, ptr, size);
X free (ptr);
X return result;
X }
X }
X
X /* The new size is a large allocation as well;
X see if we can hold it in place. */
X blocks = BLOCKIFY (size);
X if (blocks < _heapinfo[block].busy.info.size)
X {
X /* The new size is smaller; return
X excess memory to the free list. */
X _heapinfo[block + blocks].busy.type = 0;
X _heapinfo[block + blocks].busy.info.size
X = _heapinfo[block].busy.info.size - blocks;
X _heapinfo[block].busy.info.size = blocks;
X free (ADDRESS (block + blocks));
X result = ptr;
X }
X else if (blocks == _heapinfo[block].busy.info.size)
X /* No size change necessary. */
X result = ptr;
X else
X {
X /* Won't fit, so allocate a new region that will.
X Free the old region first in case there is sufficient
X adjacent free space to grow without moving. */
X blocks = _heapinfo[block].busy.info.size;
X /* Prevent free from actually returning memory to the system. */
X oldlimit = _heaplimit;
X _heaplimit = 0;
X free (ptr);
X _heaplimit = oldlimit;
X result = malloc (size);
X if (result == NULL)
X {
X (void) malloc (blocks * BLOCKSIZE);
X return NULL;
X }
X if (ptr != result)
X memmove (result, ptr, blocks * BLOCKSIZE);
X }
X break;
X
X default:
X /* Old size is a fragment; type is logarithm
X to base two of the fragment size. */
X if (size > (size_t) (1 << (type - 1)) && size <= (size_t) (1 << type))
X /* The new size is the same kind of fragment. */
X result = ptr;
X else
X {
X /* The new size is different; allocate a new space,
X and copy the lesser of the new size and the old. */
X result = malloc (size);
X if (result == NULL)
X return NULL;
X memcpy (result, ptr, min (size, (size_t) 1 << type));
X free (ptr);
X }
X break;
X }
X
X return result;
X}
X/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
XThis file is part of the GNU C Library.
X
XThe GNU C Library is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XThe GNU C Library is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with the GNU C Library; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#ifndef _MALLOC_INTERNAL
X#define _MALLOC_INTERNAL
X#include <malloc.h>
X#endif
X
X#ifndef __GNU_LIBRARY__
X#define __sbrk sbrk
X#endif
X
Xextern __ptr_t __sbrk __P ((int increment));
X
X#ifndef NULL
X#define NULL 0
X#endif
X
X/* Allocate INCREMENT more bytes of data space,
X and return the start of data space, or NULL on errors.
X If INCREMENT is negative, shrink data space. */
X__ptr_t
X__default_morecore (increment)
X ptrdiff_t increment;
X{
X __ptr_t result = __sbrk ((int) increment);
X if (result == (__ptr_t) -1)
X return NULL;
X return result;
X}
X/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
XThis library is free software; you can redistribute it and/or
Xmodify it under the terms of the GNU Library General Public License as
Xpublished by the Free Software Foundation; either version 2 of the
XLicense, or (at your option) any later version.
X
XThis library is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
XLibrary General Public License for more details.
X
XYou should have received a copy of the GNU Library General Public
XLicense along with this library; see the file COPYING.LIB. If
Xnot, write to the Free Software Foundation, Inc., 675 Mass Ave,
XCambridge, MA 02139, USA. */
X
X#ifndef _MALLOC_INTERNAL
X#define _MALLOC_INTERNAL
X#include <malloc.h>
X#endif
X
X__ptr_t
Xmemalign (alignment, size)
X size_t alignment;
X size_t size;
X{
X __ptr_t result;
X unsigned long int adj;
X
X size = ((size + alignment - 1) / alignment) * alignment;
X
X result = malloc (size);
X if (result == NULL)
X return NULL;
X adj = (unsigned long int) ((unsigned long int) ((char *) result -
X (char *) NULL)) % alignment;
X if (adj != 0)
X {
X struct alignlist *l;
X for (l = _aligned_blocks; l != NULL; l = l->next)
X if (l->aligned == NULL)
X /* This slot is free. Use it. */
X break;
X if (l == NULL)
X {
X l = (struct alignlist *) malloc (sizeof (struct alignlist));
X if (l == NULL)
X {
X free (result);
X return NULL;
X }
X }
X l->exact = result;
X result = l->aligned = (char *) result + alignment - adj;
X l->next = _aligned_blocks;
X _aligned_blocks = l;
X }
X
X return result;
X}
END_OF_FILE
if test 30710 -ne `wc -c <'malloc.c'`; then
echo shar: \"'malloc.c'\" unpacked with wrong size!
fi
# end of 'malloc.c'
fi
if test -f 'getopt1.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'getopt1.c'\"
else
echo shar: Extracting \"'getopt1.c'\" \(3486 characters\)
sed "s/^X//" >'getopt1.c' <<'END_OF_FILE'
X/* Getopt for GNU.
X Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc.
X
X This program is free software; you can redistribute it and/or modify it
X under the terms of the GNU General Public License as published by the
X Free Software Foundation; either version 2, or (at your option) any
X later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#ifdef HAVE_CONFIG_H
X#include "config.h"
X#endif
X
X#include "getopt.h"
X
X#if !__STDC__ && !defined(const) && IN_GCC
X#define const
X#endif
X
X#include <stdio.h>
X
X/* This needs to come after some library #include
X to get __GNU_LIBRARY__ defined. */
X#ifdef __GNU_LIBRARY__
X#include <stdlib.h>
X#else
Xchar *getenv ();
X#endif
X
X#ifndef NULL
X#define NULL 0
X#endif
X
Xint
Xgetopt_long (argc, argv, options, long_options, opt_index)
X int argc;
X char *const *argv;
X const char *options;
X const struct option *long_options;
X int *opt_index;
X{
X return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
X}
X
X/* Like getopt_long, but '-' as well as '--' can indicate a long option.
X If an option that starts with '-' (not '--') doesn't match a long option,
X but does match a short option, it is parsed as a short option
X instead. */
X
Xint
Xgetopt_long_only (argc, argv, options, long_options, opt_index)
X int argc;
X char *const *argv;
X const char *options;
X const struct option *long_options;
X int *opt_index;
X{
X return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
X}
X
X#ifdef TEST
X
X#include <stdio.h>
X
Xint
Xmain (argc, argv)
X int argc;
X char **argv;
X{
X int c;
X int digit_optind = 0;
X
X while (1)
X {
X int this_option_optind = optind ? optind : 1;
X int option_index = 0;
X static struct option long_options[] =
X {
X {"add", 1, 0, 0},
X {"append", 0, 0, 0},
X {"delete", 1, 0, 0},
X {"verbose", 0, 0, 0},
X {"create", 0, 0, 0},
X {"file", 1, 0, 0},
X {0, 0, 0, 0}
X };
X
X c = getopt_long (argc, argv, "abc:d:0123456789",
X long_options, &option_index);
X if (c == EOF)
X break;
X
X switch (c)
X {
X case 0:
X printf ("option %s", long_options[option_index].name);
X if (optarg)
X printf (" with arg %s", optarg);
X printf ("\n");
X break;
X
X case '0':
X case '1':
X case '2':
X case '3':
X case '4':
X case '5':
X case '6':
X case '7':
X case '8':
X case '9':
X if (digit_optind != 0 && digit_optind != this_option_optind)
X printf ("digits occur in two different argv-elements.\n");
X digit_optind = this_option_optind;
X printf ("option %c\n", c);
X break;
X
X case 'a':
X printf ("option a\n");
X break;
X
X case 'b':
X printf ("option b\n");
X break;
X
X case 'c':
X printf ("option c with value `%s'\n", optarg);
X break;
X
X case 'd':
X printf ("option d with value `%s'\n", optarg);
X break;
X
X case '?':
X break;
X
X default:
X printf ("?? getopt returned character code 0%o ??\n", c);
X }
X }
X
X if (optind < argc)
X {
X printf ("non-option ARGV-elements: ");
X while (optind < argc)
X printf ("%s ", argv[optind++]);
X printf ("\n");
X }
X
X exit (0);
X}
X
X#endif /* TEST */
END_OF_FILE
if test 3486 -ne `wc -c <'getopt1.c'`; then
echo shar: \"'getopt1.c'\" unpacked with wrong size!
fi
# end of 'getopt1.c'
fi
if test -f 'regex.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'regex.c'\"
else
echo shar: Extracting \"'regex.c'\" \(161195 characters\)
sed "s/^X//" >'regex.c' <<'END_OF_FILE'
X/* Extended regular expression matching and search library,
X version 0.11.
X (Implements POSIX draft P10003.2/D11.2, except for
X internationalization features.)
X
X Copyright (C) 1993 Free Software Foundation, Inc.
X
X This program is free software; you can redistribute it and/or modify
X it under the terms of the GNU General Public License as published by
X the Free Software Foundation; either version 2, or (at your option)
X any later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* AIX requires this to be the first thing in the file. */
X#if defined (_AIX) && !defined (REGEX_MALLOC)
X #pragma alloca
X#endif
X
X#define _GNU_SOURCE
X
X/* We need this for `regex.h', and perhaps for the Emacs include files. */
X#include <sys/types.h>
X
X#ifdef HAVE_CONFIG_H
X#include "config.h"
X#endif
X
X/* The `emacs' switch turns on certain matching commands
X that make sense only in Emacs. */
X#ifdef emacs
X
X#include "lisp.h"
X#include "buffer.h"
X#include "syntax.h"
X
X/* Emacs uses `NULL' as a predicate. */
X#undef NULL
X
X#else /* not emacs */
X
X/* We used to test for `BSTRING' here, but only GCC and Emacs define
X `BSTRING', as far as I know, and neither of them use this code. */
X#if HAVE_STRING_H || STDC_HEADERS
X#include <string.h>
X#ifndef bcmp
X#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
X#endif
X#ifndef bcopy
X#define bcopy(s, d, n) memcpy ((d), (s), (n))
X#endif
X#ifndef bzero
X#define bzero(s, n) memset ((s), 0, (n))
X#endif
X#else
X#include <strings.h>
X#endif
X
X#ifdef STDC_HEADERS
X#include <stdlib.h>
X#else
Xchar *malloc ();
Xchar *realloc ();
X#endif
X
X
X/* Define the syntax stuff for \<, \>, etc. */
X
X/* This must be nonzero for the wordchar and notwordchar pattern
X commands in re_match_2. */
X#ifndef Sword
X#define Sword 1
X#endif
X
X#ifdef SYNTAX_TABLE
X
Xextern char *re_syntax_table;
X
X#else /* not SYNTAX_TABLE */
X
X/* How many characters in the character set. */
X#define CHAR_SET_SIZE 256
X
Xstatic char re_syntax_table[CHAR_SET_SIZE];
X
Xstatic void
Xinit_syntax_once ()
X{
X register int c;
X static int done = 0;
X
X if (done)
X return;
X
X bzero (re_syntax_table, sizeof re_syntax_table);
X
X for (c = 'a'; c <= 'z'; c++)
X re_syntax_table[c] = Sword;
X
X for (c = 'A'; c <= 'Z'; c++)
X re_syntax_table[c] = Sword;
X
X for (c = '0'; c <= '9'; c++)
X re_syntax_table[c] = Sword;
X
X re_syntax_table['_'] = Sword;
X
X done = 1;
X}
X
X#endif /* not SYNTAX_TABLE */
X
X#define SYNTAX(c) re_syntax_table[c]
X
X#endif /* not emacs */
X
X/* Get the interface, including the syntax bits. */
X#include "regex.h"
X
X/* isalpha etc. are used for the character classes. */
X#include <ctype.h>
X
X#ifndef isascii
X#define isascii(c) 1
X#endif
X
X#ifdef isblank
X#define ISBLANK(c) (isascii (c) && isblank (c))
X#else
X#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
X#endif
X#ifdef isgraph
X#define ISGRAPH(c) (isascii (c) && isgraph (c))
X#else
X#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c))
X#endif
X
X#define ISPRINT(c) (isascii (c) && isprint (c))
X#define ISDIGIT(c) (isascii (c) && isdigit (c))
X#define ISALNUM(c) (isascii (c) && isalnum (c))
X#define ISALPHA(c) (isascii (c) && isalpha (c))
X#define ISCNTRL(c) (isascii (c) && iscntrl (c))
X#define ISLOWER(c) (isascii (c) && islower (c))
X#define ISPUNCT(c) (isascii (c) && ispunct (c))
X#define ISSPACE(c) (isascii (c) && isspace (c))
X#define ISUPPER(c) (isascii (c) && isupper (c))
X#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
X
X#ifndef NULL
X#define NULL 0
X#endif
X
X/* We remove any previous definition of `SIGN_EXTEND_CHAR',
X since ours (we hope) works properly with all combinations of
X machines, compilers, `char' and `unsigned char' argument types.
X (Per Bothner suggested the basic approach.) */
X#undef SIGN_EXTEND_CHAR
X#if __STDC__
X#define SIGN_EXTEND_CHAR(c) ((signed char) (c))
X#else /* not __STDC__ */
X/* As in Harbison and Steele. */
X#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
X#endif
X
X/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we
X use `alloca' instead of `malloc'. This is because using malloc in
X re_search* or re_match* could cause memory leaks when C-g is used in
X Emacs; also, malloc is slower and causes storage fragmentation. On
X the other hand, malloc is more portable, and easier to debug.
X
X Because we sometimes use alloca, some routines have to be macros,
X not functions -- `alloca'-allocated space disappears at the end of the
X function it is called in. */
X
X#ifdef REGEX_MALLOC
X
X#define REGEX_ALLOCATE malloc
X#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
X
X#else /* not REGEX_MALLOC */
X
X/* Emacs already defines alloca, sometimes. */
X#ifndef alloca
X
X/* Make alloca work the best possible way. */
X#ifdef __GNUC__
X#define alloca __builtin_alloca
X#else /* not __GNUC__ */
X#if HAVE_ALLOCA_H
X#include <alloca.h>
X#else /* not __GNUC__ or HAVE_ALLOCA_H */
X#ifndef _AIX /* Already did AIX, up at the top. */
Xchar *alloca ();
X#endif /* not _AIX */
X#endif /* not HAVE_ALLOCA_H */
X#endif /* not __GNUC__ */
X
X#endif /* not alloca */
X
X#define REGEX_ALLOCATE alloca
X
X/* Assumes a `char *destination' variable. */
X#define REGEX_REALLOCATE(source, osize, nsize) \
X (destination = (char *) alloca (nsize), \
X bcopy (source, destination, osize), \
X destination)
X
X#endif /* not REGEX_MALLOC */
X
X
X/* True if `size1' is non-NULL and PTR is pointing anywhere inside
X `string1' or just past its end. This works if PTR is NULL, which is
X a good thing. */
X#define FIRST_STRING_P(ptr) \
X (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
X
X/* (Re)Allocate N items of type T using malloc, or fail. */
X#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
X#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
X#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
X
X#define BYTEWIDTH 8 /* In bits. */
X
X#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
X
X#define MAX(a, b) ((a) > (b) ? (a) : (b))
X#define MIN(a, b) ((a) < (b) ? (a) : (b))
X
Xtypedef char boolean;
X#define false 0
X#define true 1
X
X/* These are the command codes that appear in compiled regular
X expressions. Some opcodes are followed by argument bytes. A
X command code can specify any interpretation whatsoever for its
X arguments. Zero bytes may appear in the compiled regular expression.
X
X The value of `exactn' is needed in search.c (search_buffer) in Emacs.
X So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of
X `exactn' we use here must also be 1. */
X
Xtypedef enum
X{
X no_op = 0,
X
X /* Followed by one byte giving n, then by n literal bytes. */
X exactn = 1,
X
X /* Matches any (more or less) character. */
X anychar,
X
X /* Matches any one char belonging to specified set. First
X following byte is number of bitmap bytes. Then come bytes
X for a bitmap saying which chars are in. Bits in each byte
X are ordered low-bit-first. A character is in the set if its
X bit is 1. A character too large to have a bit in the map is
X automatically not in the set. */
X charset,
X
X /* Same parameters as charset, but match any character that is
X not one of those specified. */
X charset_not,
X
X /* Start remembering the text that is matched, for storing in a
X register. Followed by one byte with the register number, in
X the range 0 to one less than the pattern buffer's re_nsub
X field. Then followed by one byte with the number of groups
X inner to this one. (This last has to be part of the
X start_memory only because we need it in the on_failure_jump
X of re_match_2.) */
X start_memory,
X
X /* Stop remembering the text that is matched and store it in a
X memory register. Followed by one byte with the register
X number, in the range 0 to one less than `re_nsub' in the
X pattern buffer, and one byte with the number of inner groups,
X just like `start_memory'. (We need the number of inner
X groups here because we don't have any easy way of finding the
X corresponding start_memory when we're at a stop_memory.) */
X stop_memory,
X
X /* Match a duplicate of something remembered. Followed by one
X byte containing the register number. */
X duplicate,
X
X /* Fail unless at beginning of line. */
X begline,
X
X /* Fail unless at end of line. */
X endline,
X
X /* Succeeds if at beginning of buffer (if emacs) or at beginning
X of string to be matched (if not). */
X begbuf,
X
X /* Analogously, for end of buffer/string. */
X endbuf,
X
X /* Followed by two byte relative address to which to jump. */
X jump,
X
X /* Same as jump, but marks the end of an alternative. */
X jump_past_alt,
X
X /* Followed by two-byte relative address of place to resume at
X in case of failure. */
X on_failure_jump,
X
X /* Like on_failure_jump, but pushes a placeholder instead of the
X current string position when executed. */
X on_failure_keep_string_jump,
X
X /* Throw away latest failure point and then jump to following
X two-byte relative address. */
X pop_failure_jump,
X
X /* Change to pop_failure_jump if know won't have to backtrack to
X match; otherwise change to jump. This is used to jump
X back to the beginning of a repeat. If what follows this jump
X clearly won't match what the repeat does, such that we can be
X sure that there is no use backtracking out of repetitions
X already matched, then we change it to a pop_failure_jump.
X Followed by two-byte address. */
X maybe_pop_jump,
X
X /* Jump to following two-byte address, and push a dummy failure
X point. This failure point will be thrown away if an attempt
X is made to use it for a failure. A `+' construct makes this
X before the first repeat. Also used as an intermediary kind
X of jump when compiling an alternative. */
X dummy_failure_jump,
X
X /* Push a dummy failure point and continue. Used at the end of
X alternatives. */
X push_dummy_failure,
X
X /* Followed by two-byte relative address and two-byte number n.
X After matching N times, jump to the address upon failure. */
X succeed_n,
X
X /* Followed by two-byte relative address, and two-byte number n.
X Jump to the address N times, then fail. */
X jump_n,
X
X /* Set the following two-byte relative address to the
X subsequent two-byte number. The address *includes* the two
X bytes of number. */
X set_number_at,
X
X wordchar, /* Matches any word-constituent character. */
X notwordchar, /* Matches any char that is not a word-constituent. */
X
X wordbeg, /* Succeeds if at word beginning. */
X wordend, /* Succeeds if at word end. */
X
X wordbound, /* Succeeds if at a word boundary. */
X notwordbound /* Succeeds if not at a word boundary. */
X
X#ifdef emacs
X ,before_dot, /* Succeeds if before point. */
X at_dot, /* Succeeds if at point. */
X after_dot, /* Succeeds if after point. */
X
X /* Matches any character whose syntax is specified. Followed by
X a byte which contains a syntax code, e.g., Sword. */
X syntaxspec,
X
X /* Matches any character whose syntax is not that specified. */
X notsyntaxspec
X#endif /* emacs */
X} re_opcode_t;
X
X/* Common operations on the compiled pattern. */
X
X/* Store NUMBER in two contiguous bytes starting at DESTINATION. */
X
X#define STORE_NUMBER(destination, number) \
X do { \
X (destination)[0] = (number) & 0377; \
X (destination)[1] = (number) >> 8; \
X } while (0)
X
X/* Same as STORE_NUMBER, except increment DESTINATION to
X the byte after where the number is stored. Therefore, DESTINATION
X must be an lvalue. */
X
X#define STORE_NUMBER_AND_INCR(destination, number) \
X do { \
X STORE_NUMBER (destination, number); \
X (destination) += 2; \
X } while (0)
X
X/* Put into DESTINATION a number stored in two contiguous bytes starting
X at SOURCE. */
X
X#define EXTRACT_NUMBER(destination, source) \
X do { \
X (destination) = *(source) & 0377; \
X (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \
X } while (0)
X
X#ifdef DEBUG
Xstatic void
Xextract_number (dest, source)
X int *dest;
X unsigned char *source;
X{
X int temp = SIGN_EXTEND_CHAR (*(source + 1));
X *dest = *source & 0377;
X *dest += temp << 8;
X}
X
X#ifndef EXTRACT_MACROS /* To debug the macros. */
X#undef EXTRACT_NUMBER
X#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src)
X#endif /* not EXTRACT_MACROS */
X
X#endif /* DEBUG */
X
X/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
X SOURCE must be an lvalue. */
X
X#define EXTRACT_NUMBER_AND_INCR(destination, source) \
X do { \
X EXTRACT_NUMBER (destination, source); \
X (source) += 2; \
X } while (0)
X
X#ifdef DEBUG
Xstatic void
Xextract_number_and_incr (destination, source)
X int *destination;
X unsigned char **source;
X{
X extract_number (destination, *source);
X *source += 2;
X}
X
X#ifndef EXTRACT_MACROS
X#undef EXTRACT_NUMBER_AND_INCR
X#define EXTRACT_NUMBER_AND_INCR(dest, src) \
X extract_number_and_incr (&dest, &src)
X#endif /* not EXTRACT_MACROS */
X
X#endif /* DEBUG */
X
X/* If DEBUG is defined, Regex prints many voluminous messages about what
X it is doing (if the variable `debug' is nonzero). If linked with the
X main program in `iregex.c', you can enter patterns and strings
X interactively. And if linked with the main program in `main.c' and
X the other test files, you can run the already-written tests. */
X
X#ifdef DEBUG
X
X/* We use standard I/O for debugging. */
X#include <stdio.h>
X
X/* It is useful to test things that ``must'' be true when debugging. */
X#include <assert.h>
X
Xstatic int debug = 0;
X
X#define DEBUG_STATEMENT(e) e
X#define DEBUG_PRINT1(x) if (debug) printf (x)
X#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
X#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
X#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
X#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \
X if (debug) print_partial_compiled_pattern (s, e)
X#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \
X if (debug) print_double_string (w, s1, sz1, s2, sz2)
X
X
Xextern void printchar ();
X
X/* Print the fastmap in human-readable form. */
X
Xvoid
Xprint_fastmap (fastmap)
X char *fastmap;
X{
X unsigned was_a_range = 0;
X unsigned i = 0;
X
X while (i < (1 << BYTEWIDTH))
X {
X if (fastmap[i++])
X {
X was_a_range = 0;
X printchar (i - 1);
X while (i < (1 << BYTEWIDTH) && fastmap[i])
X {
X was_a_range = 1;
X i++;
X }
X if (was_a_range)
X {
X printf ("-");
X printchar (i - 1);
X }
X }
X }
X putchar ('\n');
X}
X
X
X/* Print a compiled pattern string in human-readable form, starting at
X the START pointer into it and ending just before the pointer END. */
X
Xvoid
Xprint_partial_compiled_pattern (start, end)
X unsigned char *start;
X unsigned char *end;
X{
X int mcnt, mcnt2;
X unsigned char *p = start;
X unsigned char *pend = end;
X
X if (start == NULL)
X {
X printf ("(null)\n");
X return;
X }
X
X /* Loop over pattern commands. */
X while (p < pend)
X {
X switch ((re_opcode_t) *p++)
X {
X case no_op:
X printf ("/no_op");
X break;
X
X case exactn:
X mcnt = *p++;
X printf ("/exactn/%d", mcnt);
X do
X {
X putchar ('/');
X printchar (*p++);
X }
X while (--mcnt);
X break;
X
X case start_memory:
X mcnt = *p++;
X printf ("/start_memory/%d/%d", mcnt, *p++);
X break;
X
X case stop_memory:
X mcnt = *p++;
X printf ("/stop_memory/%d/%d", mcnt, *p++);
X break;
X
X case duplicate:
X printf ("/duplicate/%d", *p++);
X break;
X
X case anychar:
X printf ("/anychar");
X break;
X
X case charset:
X case charset_not:
X {
X register int c;
X
X printf ("/charset%s",
X (re_opcode_t) *(p - 1) == charset_not ? "_not" : "");
X
X assert (p + *p < pend);
X
X for (c = 0; c < *p; c++)
X {
X unsigned bit;
X unsigned char map_byte = p[1 + c];
X
X putchar ('/');
X
X for (bit = 0; bit < BYTEWIDTH; bit++)
X if (map_byte & (1 << bit))
X printchar (c * BYTEWIDTH + bit);
X }
X p += 1 + *p;
X break;
X }
X
X case begline:
X printf ("/begline");
X break;
X
X case endline:
X printf ("/endline");
X break;
X
X case on_failure_jump:
X extract_number_and_incr (&mcnt, &p);
X printf ("/on_failure_jump/0/%d", mcnt);
X break;
X
X case on_failure_keep_string_jump:
X extract_number_and_incr (&mcnt, &p);
X printf ("/on_failure_keep_string_jump/0/%d", mcnt);
X break;
X
X case dummy_failure_jump:
X extract_number_and_incr (&mcnt, &p);
X printf ("/dummy_failure_jump/0/%d", mcnt);
X break;
X
X case push_dummy_failure:
X printf ("/push_dummy_failure");
X break;
X
X case maybe_pop_jump:
X extract_number_and_incr (&mcnt, &p);
X printf ("/maybe_pop_jump/0/%d", mcnt);
X break;
X
X case pop_failure_jump:
X extract_number_and_incr (&mcnt, &p);
X printf ("/pop_failure_jump/0/%d", mcnt);
X break;
X
X case jump_past_alt:
X extract_number_and_incr (&mcnt, &p);
X printf ("/jump_past_alt/0/%d", mcnt);
X break;
X
X case jump:
X extract_number_and_incr (&mcnt, &p);
X printf ("/jump/0/%d", mcnt);
X break;
X
X case succeed_n:
X extract_number_and_incr (&mcnt, &p);
X extract_number_and_incr (&mcnt2, &p);
X printf ("/succeed_n/0/%d/0/%d", mcnt, mcnt2);
X break;
X
X case jump_n:
X extract_number_and_incr (&mcnt, &p);
X extract_number_and_incr (&mcnt2, &p);
X printf ("/jump_n/0/%d/0/%d", mcnt, mcnt2);
X break;
X
X case set_number_at:
X extract_number_and_incr (&mcnt, &p);
X extract_number_and_incr (&mcnt2, &p);
X printf ("/set_number_at/0/%d/0/%d", mcnt, mcnt2);
X break;
X
X case wordbound:
X printf ("/wordbound");
X break;
X
X case notwordbound:
X printf ("/notwordbound");
X break;
X
X case wordbeg:
X printf ("/wordbeg");
X break;
X
X case wordend:
X printf ("/wordend");
X
X#ifdef emacs
X case before_dot:
X printf ("/before_dot");
X break;
X
X case at_dot:
X printf ("/at_dot");
X break;
X
X case after_dot:
X printf ("/after_dot");
X break;
X
X case syntaxspec:
X printf ("/syntaxspec");
X mcnt = *p++;
X printf ("/%d", mcnt);
X break;
X
X case notsyntaxspec:
X printf ("/notsyntaxspec");
X mcnt = *p++;
X printf ("/%d", mcnt);
X break;
X#endif /* emacs */
X
X case wordchar:
X printf ("/wordchar");
X break;
X
X case notwordchar:
X printf ("/notwordchar");
X break;
X
X case begbuf:
X printf ("/begbuf");
X break;
X
X case endbuf:
X printf ("/endbuf");
X break;
X
X default:
X printf ("?%d", *(p-1));
X }
X }
X printf ("/\n");
X}
X
X
Xvoid
Xprint_compiled_pattern (bufp)
X struct re_pattern_buffer *bufp;
X{
X unsigned char *buffer = bufp->buffer;
X
X print_partial_compiled_pattern (buffer, buffer + bufp->used);
X printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated);
X
X if (bufp->fastmap_accurate && bufp->fastmap)
X {
X printf ("fastmap: ");
X print_fastmap (bufp->fastmap);
X }
X
X printf ("re_nsub: %d\t", bufp->re_nsub);
X printf ("regs_alloc: %d\t", bufp->regs_allocated);
X printf ("can_be_null: %d\t", bufp->can_be_null);
X printf ("newline_anchor: %d\n", bufp->newline_anchor);
X printf ("no_sub: %d\t", bufp->no_sub);
X printf ("not_bol: %d\t", bufp->not_bol);
X printf ("not_eol: %d\t", bufp->not_eol);
X printf ("syntax: %d\n", bufp->syntax);
X /* Perhaps we should print the translate table? */
X}
X
X
Xvoid
Xprint_double_string (where, string1, size1, string2, size2)
X const char *where;
X const char *string1;
X const char *string2;
X int size1;
X int size2;
X{
X unsigned this_char;
X
X if (where == NULL)
X printf ("(null)");
X else
X {
X if (FIRST_STRING_P (where))
X {
X for (this_char = where - string1; this_char < size1; this_char++)
X printchar (string1[this_char]);
X
X where = string2;
X }
X
X for (this_char = where - string2; this_char < size2; this_char++)
X printchar (string2[this_char]);
X }
X}
X
X#else /* not DEBUG */
X
X#undef assert
X#define assert(e)
X
X#define DEBUG_STATEMENT(e)
X#define DEBUG_PRINT1(x)
X#define DEBUG_PRINT2(x1, x2)
X#define DEBUG_PRINT3(x1, x2, x3)
X#define DEBUG_PRINT4(x1, x2, x3, x4)
X#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
X#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
X
X#endif /* not DEBUG */
X
X/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
X also be assigned to arbitrarily: each pattern buffer stores its own
X syntax, so it can be changed between regex compilations. */
Xreg_syntax_t re_syntax_options = RE_SYNTAX_EMACS;
X
X
X/* Specify the precise syntax of regexps for compilation. This provides
X for compatibility for various utilities which historically have
X different, incompatible syntaxes.
X
X The argument SYNTAX is a bit mask comprised of the various bits
X defined in regex.h. We return the old syntax. */
X
Xreg_syntax_t
Xre_set_syntax (syntax)
X reg_syntax_t syntax;
X{
X reg_syntax_t ret = re_syntax_options;
X
X re_syntax_options = syntax;
X return ret;
X}
X
X/* This table gives an error message for each of the error codes listed
X in regex.h. Obviously the order here has to be same as there. */
X
Xstatic const char *re_error_msg[] =
X { NULL, /* REG_NOERROR */
X "No match", /* REG_NOMATCH */
X "Invalid regular expression", /* REG_BADPAT */
X "Invalid collation character", /* REG_ECOLLATE */
X "Invalid character class name", /* REG_ECTYPE */
X "Trailing backslash", /* REG_EESCAPE */
X "Invalid back reference", /* REG_ESUBREG */
X "Unmatched [ or [^", /* REG_EBRACK */
X "Unmatched ( or \\(", /* REG_EPAREN */
X "Unmatched \\{", /* REG_EBRACE */
X "Invalid content of \\{\\}", /* REG_BADBR */
X "Invalid range end", /* REG_ERANGE */
X "Memory exhausted", /* REG_ESPACE */
X "Invalid preceding regular expression", /* REG_BADRPT */
X "Premature end of regular expression", /* REG_EEND */
X "Regular expression too big", /* REG_ESIZE */
X "Unmatched ) or \\)", /* REG_ERPAREN */
X };
X
X/* Subroutine declarations and macros for regex_compile. */
X
Xstatic void store_op1 (), store_op2 ();
Xstatic void insert_op1 (), insert_op2 ();
Xstatic boolean at_begline_loc_p (), at_endline_loc_p ();
Xstatic boolean group_in_compile_stack ();
Xstatic reg_errcode_t compile_range ();
X
X/* Fetch the next character in the uncompiled pattern---translating it
X if necessary. Also cast from a signed character in the constant
X string passed to us by the user to an unsigned char that we can use
X as an array index (in, e.g., `translate'). */
X#define PATFETCH(c) \
X do {if (p == pend) return REG_EEND; \
X c = (unsigned char) *p++; \
X if (translate) c = translate[c]; \
X } while (0)
X
X/* Fetch the next character in the uncompiled pattern, with no
X translation. */
X#define PATFETCH_RAW(c) \
X do {if (p == pend) return REG_EEND; \
X c = (unsigned char) *p++; \
X } while (0)
X
X/* Go backwards one character in the pattern. */
X#define PATUNFETCH p--
X
X
X/* If `translate' is non-null, return translate[D], else just D. We
X cast the subscript to translate because some data is declared as
X `char *', to avoid warnings when a string constant is passed. But
X when we use a character as a subscript we must make it unsigned. */
X#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
X
X
X/* Macros for outputting the compiled pattern into `buffer'. */
X
X/* If the buffer isn't allocated when it comes in, use this. */
X#define INIT_BUF_SIZE 32
X
X/* Make sure we have at least N more bytes of space in buffer. */
X#define GET_BUFFER_SPACE(n) \
X while (b - bufp->buffer + (n) > bufp->allocated) \
X EXTEND_BUFFER ()
X
X/* Make sure we have one more byte of buffer space and then add C to it. */
X#define BUF_PUSH(c) \
X do { \
X GET_BUFFER_SPACE (1); \
X *b++ = (unsigned char) (c); \
X } while (0)
X
X
X/* Ensure we have two more bytes of buffer space and then append C1 and C2. */
X#define BUF_PUSH_2(c1, c2) \
X do { \
X GET_BUFFER_SPACE (2); \
X *b++ = (unsigned char) (c1); \
X *b++ = (unsigned char) (c2); \
X } while (0)
X
X
X/* As with BUF_PUSH_2, except for three bytes. */
X#define BUF_PUSH_3(c1, c2, c3) \
X do { \
X GET_BUFFER_SPACE (3); \
X *b++ = (unsigned char) (c1); \
X *b++ = (unsigned char) (c2); \
X *b++ = (unsigned char) (c3); \
X } while (0)
X
X
X/* Store a jump with opcode OP at LOC to location TO. We store a
X relative address offset by the three bytes the jump itself occupies. */
X#define STORE_JUMP(op, loc, to) \
X store_op1 (op, loc, (to) - (loc) - 3)
X
X/* Likewise, for a two-argument jump. */
X#define STORE_JUMP2(op, loc, to, arg) \
X store_op2 (op, loc, (to) - (loc) - 3, arg)
X
X/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */
X#define INSERT_JUMP(op, loc, to) \
X insert_op1 (op, loc, (to) - (loc) - 3, b)
X
X/* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */
X#define INSERT_JUMP2(op, loc, to, arg) \
X insert_op2 (op, loc, (to) - (loc) - 3, arg, b)
X
X
X/* This is not an arbitrary limit: the arguments which represent offsets
X into the pattern are two bytes long. So if 2^16 bytes turns out to
X be too small, many things would have to change. */
X#define MAX_BUF_SIZE (1L << 16)
X
X
X/* Extend the buffer by twice its current size via realloc and
X reset the pointers that pointed into the old block to point to the
X correct places in the new one. If extending the buffer results in it
X being larger than MAX_BUF_SIZE, then flag memory exhausted. */
X#define EXTEND_BUFFER() \
X do { \
X unsigned char *old_buffer = bufp->buffer; \
X if (bufp->allocated == MAX_BUF_SIZE) \
X return REG_ESIZE; \
X bufp->allocated <<= 1; \
X if (bufp->allocated > MAX_BUF_SIZE) \
X bufp->allocated = MAX_BUF_SIZE; \
X bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\
X if (bufp->buffer == NULL) \
X return REG_ESPACE; \
X /* If the buffer moved, move all the pointers into it. */ \
X if (old_buffer != bufp->buffer) \
X { \
X b = (b - old_buffer) + bufp->buffer; \
X begalt = (begalt - old_buffer) + bufp->buffer; \
X if (fixup_alt_jump) \
X fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
X if (laststart) \
X laststart = (laststart - old_buffer) + bufp->buffer; \
X if (pending_exact) \
X pending_exact = (pending_exact - old_buffer) + bufp->buffer; \
X } \
X } while (0)
X
X
X/* Since we have one byte reserved for the register number argument to
X {start,stop}_memory, the maximum number of groups we can report
X things about is what fits in that byte. */
X#define MAX_REGNUM 255
X
X/* But patterns can have more than `MAX_REGNUM' registers. We just
X ignore the excess. */
Xtypedef unsigned regnum_t;
X
X
X/* Macros for the compile stack. */
X
X/* Since offsets can go either forwards or backwards, this type needs to
X be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */
Xtypedef int pattern_offset_t;
X
Xtypedef struct
X{
X pattern_offset_t begalt_offset;
X pattern_offset_t fixup_alt_jump;
X pattern_offset_t inner_group_offset;
X pattern_offset_t laststart_offset;
X regnum_t regnum;
X} compile_stack_elt_t;
X
X
Xtypedef struct
X{
X compile_stack_elt_t *stack;
X unsigned size;
X unsigned avail; /* Offset of next open position. */
X} compile_stack_type;
X
X
X#define INIT_COMPILE_STACK_SIZE 32
X
X#define COMPILE_STACK_EMPTY (compile_stack.avail == 0)
X#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size)
X
X/* The next available element. */
X#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
X
X
X/* Set the bit for character C in a list. */
X#define SET_LIST_BIT(c) \
X (b[((unsigned char) (c)) / BYTEWIDTH] \
X |= 1 << (((unsigned char) c) % BYTEWIDTH))
X
X
X/* Get the next unsigned number in the uncompiled pattern. */
X#define GET_UNSIGNED_NUMBER(num) \
X { if (p != pend) \
X { \
X PATFETCH (c); \
X while (ISDIGIT (c)) \
X { \
X if (num < 0) \
X num = 0; \
X num = num * 10 + c - '0'; \
X if (p == pend) \
X break; \
X PATFETCH (c); \
X } \
X } \
X }
X
X#define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
X
X#define IS_CHAR_CLASS(string) \
X (STREQ (string, "alpha") || STREQ (string, "upper") \
X || STREQ (string, "lower") || STREQ (string, "digit") \
X || STREQ (string, "alnum") || STREQ (string, "xdigit") \
X || STREQ (string, "space") || STREQ (string, "print") \
X || STREQ (string, "punct") || STREQ (string, "graph") \
X || STREQ (string, "cntrl") || STREQ (string, "blank"))
X
X/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
X Returns one of error codes defined in `regex.h', or zero for success.
X
X Assumes the `allocated' (and perhaps `buffer') and `translate'
X fields are set in BUFP on entry.
X
X If it succeeds, results are put in BUFP (if it returns an error, the
X contents of BUFP are undefined):
X `buffer' is the compiled pattern;
X `syntax' is set to SYNTAX;
X `used' is set to the length of the compiled pattern;
X `fastmap_accurate' is zero;
X `re_nsub' is the number of subexpressions in PATTERN;
X `not_bol' and `not_eol' are zero;
X
X The `fastmap' and `newline_anchor' fields are neither
X examined nor set. */
X
Xstatic reg_errcode_t
Xregex_compile (pattern, size, syntax, bufp)
X const char *pattern;
X int size;
X reg_syntax_t syntax;
X struct re_pattern_buffer *bufp;
X{
X /* We fetch characters from PATTERN here. Even though PATTERN is
X `char *' (i.e., signed), we declare these variables as unsigned, so
X they can be reliably used as array indices. */
X register unsigned char c, c1;
X
X /* A random tempory spot in PATTERN. */
X const char *p1;
X
X /* Points to the end of the buffer, where we should append. */
X register unsigned char *b;
X
X /* Keeps track of unclosed groups. */
X compile_stack_type compile_stack;
X
X /* Points to the current (ending) position in the pattern. */
X const char *p = pattern;
X const char *pend = pattern + size;
X
X /* How to translate the characters in the pattern. */
X char *translate = bufp->translate;
X
X /* Address of the count-byte of the most recently inserted `exactn'
X command. This makes it possible to tell if a new exact-match
X character can be added to that command or if the character requires
X a new `exactn' command. */
X unsigned char *pending_exact = 0;
X
X /* Address of start of the most recently finished expression.
X This tells, e.g., postfix * where to find the start of its
X operand. Reset at the beginning of groups and alternatives. */
X unsigned char *laststart = 0;
X
X /* Address of beginning of regexp, or inside of last group. */
X unsigned char *begalt;
X
X /* Place in the uncompiled pattern (i.e., the {) to
X which to go back if the interval is invalid. */
X const char *beg_interval;
X
X /* Address of the place where a forward jump should go to the end of
X the containing expression. Each alternative of an `or' -- except the
X last -- ends with a forward jump of this sort. */
X unsigned char *fixup_alt_jump = 0;
X
X /* Counts open-groups as they are encountered. Remembered for the
X matching close-group on the compile stack, so the same register
X number is put in the stop_memory as the start_memory. */
X regnum_t regnum = 0;
X
X#ifdef DEBUG
X DEBUG_PRINT1 ("\nCompiling pattern: ");
X if (debug)
X {
X unsigned debug_count;
X
X for (debug_count = 0; debug_count < size; debug_count++)
X printchar (pattern[debug_count]);
X putchar ('\n');
X }
X#endif /* DEBUG */
X
X /* Initialize the compile stack. */
X compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
X if (compile_stack.stack == NULL)
X return REG_ESPACE;
X
X compile_stack.size = INIT_COMPILE_STACK_SIZE;
X compile_stack.avail = 0;
X
X /* Initialize the pattern buffer. */
X bufp->syntax = syntax;
X bufp->fastmap_accurate = 0;
X bufp->not_bol = bufp->not_eol = 0;
X
X /* Set `used' to zero, so that if we return an error, the pattern
X printer (for debugging) will think there's no pattern. We reset it
X at the end. */
X bufp->used = 0;
X
X /* Always count groups, whether or not bufp->no_sub is set. */
X bufp->re_nsub = 0;
X
X#if !defined (emacs) && !defined (SYNTAX_TABLE)
X /* Initialize the syntax table. */
X init_syntax_once ();
X#endif
X
X if (bufp->allocated == 0)
X {
X if (bufp->buffer)
X { /* If zero allocated, but buffer is non-null, try to realloc
X enough space. This loses if buffer's address is bogus, but
X that is the user's responsibility. */
X RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
X }
X else
X { /* Caller did not allocate a buffer. Do it for them. */
X bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
X }
X if (!bufp->buffer) return REG_ESPACE;
X
X bufp->allocated = INIT_BUF_SIZE;
X }
X
X begalt = b = bufp->buffer;
X
X /* Loop through the uncompiled pattern until we're at the end. */
X while (p != pend)
X {
X PATFETCH (c);
X
X switch (c)
X {
X case '^':
X {
X if ( /* If at start of pattern, it's an operator. */
X p == pattern + 1
X /* If context independent, it's an operator. */
X || syntax & RE_CONTEXT_INDEP_ANCHORS
X /* Otherwise, depends on what's come before. */
X || at_begline_loc_p (pattern, p, syntax))
X BUF_PUSH (begline);
X else
X goto normal_char;
X }
X break;
X
X
X case '$':
X {
X if ( /* If at end of pattern, it's an operator. */
X p == pend
X /* If context independent, it's an operator. */
X || syntax & RE_CONTEXT_INDEP_ANCHORS
X /* Otherwise, depends on what's next. */
X || at_endline_loc_p (p, pend, syntax))
X BUF_PUSH (endline);
X else
X goto normal_char;
X }
X break;
X
X
X case '+':
X case '?':
X if ((syntax & RE_BK_PLUS_QM)
X || (syntax & RE_LIMITED_OPS))
X goto normal_char;
X handle_plus:
X case '*':
X /* If there is no previous pattern... */
X if (!laststart)
X {
X if (syntax & RE_CONTEXT_INVALID_OPS)
X return REG_BADRPT;
X else if (!(syntax & RE_CONTEXT_INDEP_OPS))
X goto normal_char;
X }
X
X {
X /* Are we optimizing this jump? */
X boolean keep_string_p = false;
X
X /* 1 means zero (many) matches is allowed. */
X char zero_times_ok = 0, many_times_ok = 0;
X
X /* If there is a sequence of repetition chars, collapse it
X down to just one (the right one). We can't combine
X interval operators with these because of, e.g., `a{2}*',
X which should only match an even number of `a's. */
X
X for (;;)
X {
X zero_times_ok |= c != '+';
X many_times_ok |= c != '?';
X
X if (p == pend)
X break;
X
X PATFETCH (c);
X
X if (c == '*'
X || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
X ;
X
X else if (syntax & RE_BK_PLUS_QM && c == '\\')
X {
X if (p == pend) return REG_EESCAPE;
X
X PATFETCH (c1);
X if (!(c1 == '+' || c1 == '?'))
X {
X PATUNFETCH;
X PATUNFETCH;
X break;
X }
X
X c = c1;
X }
X else
X {
X PATUNFETCH;
X break;
X }
X
X /* If we get here, we found another repeat character. */
X }
X
X /* Star, etc. applied to an empty pattern is equivalent
X to an empty pattern. */
X if (!laststart)
X break;
X
X /* Now we know whether or not zero matches is allowed
X and also whether or not two or more matches is allowed. */
X if (many_times_ok)
X { /* More than one repetition is allowed, so put in at the
X end a backward relative jump from `b' to before the next
X jump we're going to put in below (which jumps from
X laststart to after this jump).
X
X But if we are at the `*' in the exact sequence `.*\n',
X insert an unconditional jump backwards to the .,
X instead of the beginning of the loop. This way we only
X push a failure point once, instead of every time
X through the loop. */
X assert (p - 1 > pattern);
X
X /* Allocate the space for the jump. */
X GET_BUFFER_SPACE (3);
X
X /* We know we are not at the first character of the pattern,
X because laststart was nonzero. And we've already
X incremented `p', by the way, to be the character after
X the `*'. Do we have to do something analogous here
X for null bytes, because of RE_DOT_NOT_NULL? */
X if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
X && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
X && !(syntax & RE_DOT_NEWLINE))
X { /* We have .*\n. */
X STORE_JUMP (jump, b, laststart);
X keep_string_p = true;
X }
X else
X /* Anything else. */
X STORE_JUMP (maybe_pop_jump, b, laststart - 3);
X
X /* We've added more stuff to the buffer. */
X b += 3;
X }
X
X /* On failure, jump from laststart to b + 3, which will be the
X end of the buffer after this jump is inserted. */
X GET_BUFFER_SPACE (3);
X INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
X : on_failure_jump,
X laststart, b + 3);
X pending_exact = 0;
X b += 3;
X
X if (!zero_times_ok)
X {
X /* At least one repetition is required, so insert a
X `dummy_failure_jump' before the initial
X `on_failure_jump' instruction of the loop. This
X effects a skip over that instruction the first time
X we hit that loop. */
X GET_BUFFER_SPACE (3);
X INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6);
X b += 3;
X }
X }
X break;
X
X
X case '.':
X laststart = b;
X BUF_PUSH (anychar);
X break;
X
X
X case '[':
X {
X boolean had_char_class = false;
X
X if (p == pend) return REG_EBRACK;
X
X /* Ensure that we have enough space to push a charset: the
X opcode, the length count, and the bitset; 34 bytes in all. */
X GET_BUFFER_SPACE (34);
X
X laststart = b;
X
X /* We test `*p == '^' twice, instead of using an if
X statement, so we only need one BUF_PUSH. */
X BUF_PUSH (*p == '^' ? charset_not : charset);
X if (*p == '^')
X p++;
X
X /* Remember the first position in the bracket expression. */
X p1 = p;
X
X /* Push the number of bytes in the bitmap. */
X BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
X
X /* Clear the whole map. */
X bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
X
X /* charset_not matches newline according to a syntax bit. */
X if ((re_opcode_t) b[-2] == charset_not
X && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
X SET_LIST_BIT ('\n');
X
X /* Read in characters and ranges, setting map bits. */
X for (;;)
X {
X if (p == pend) return REG_EBRACK;
X
X PATFETCH (c);
X
X /* \ might escape characters inside [...] and [^...]. */
X if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
X {
X if (p == pend) return REG_EESCAPE;
X
X PATFETCH (c1);
X SET_LIST_BIT (c1);
X continue;
X }
X
X /* Could be the end of the bracket expression. If it's
X not (i.e., when the bracket expression is `[]' so
X far), the ']' character bit gets set way below. */
X if (c == ']' && p != p1 + 1)
X break;
X
X /* Look ahead to see if it's a range when the last thing
X was a character class. */
X if (had_char_class && c == '-' && *p != ']')
X return REG_ERANGE;
X
X /* Look ahead to see if it's a range when the last thing
X was a character: if this is a hyphen not at the
X beginning or the end of a list, then it's the range
X operator. */
X if (c == '-'
X && !(p - 2 >= pattern && p[-2] == '[')
X && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
X && *p != ']')
X {
X reg_errcode_t ret
X = compile_range (&p, pend, translate, syntax, b);
X if (ret != REG_NOERROR) return ret;
X }
X
X else if (p[0] == '-' && p[1] != ']')
X { /* This handles ranges made up of characters only. */
X reg_errcode_t ret;
X
X /* Move past the `-'. */
X PATFETCH (c1);
X
X ret = compile_range (&p, pend, translate, syntax, b);
X if (ret != REG_NOERROR) return ret;
X }
X
X /* See if we're at the beginning of a possible character
X class. */
X
X else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
X { /* Leave room for the null. */
X char str[CHAR_CLASS_MAX_LENGTH + 1];
X
X PATFETCH (c);
X c1 = 0;
X
X /* If pattern is `[[:'. */
X if (p == pend) return REG_EBRACK;
X
X for (;;)
X {
X PATFETCH (c);
X if (c == ':' || c == ']' || p == pend
X || c1 == CHAR_CLASS_MAX_LENGTH)
X break;
X str[c1++] = c;
X }
X str[c1] = '\0';
X
X /* If isn't a word bracketed by `[:' and:`]':
X undo the ending character, the letters, and leave
X the leading `:' and `[' (but set bits for them). */
X if (c == ':' && *p == ']')
X {
X int ch;
X boolean is_alnum = STREQ (str, "alnum");
X boolean is_alpha = STREQ (str, "alpha");
X boolean is_blank = STREQ (str, "blank");
X boolean is_cntrl = STREQ (str, "cntrl");
X boolean is_digit = STREQ (str, "digit");
X boolean is_graph = STREQ (str, "graph");
X boolean is_lower = STREQ (str, "lower");
X boolean is_print = STREQ (str, "print");
X boolean is_punct = STREQ (str, "punct");
X boolean is_space = STREQ (str, "space");
X boolean is_upper = STREQ (str, "upper");
X boolean is_xdigit = STREQ (str, "xdigit");
X
X if (!IS_CHAR_CLASS (str)) return REG_ECTYPE;
X
X /* Throw away the ] at the end of the character
X class. */
X PATFETCH (c);
X
X if (p == pend) return REG_EBRACK;
X
X for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
X {
X if ( (is_alnum && ISALNUM (ch))
X || (is_alpha && ISALPHA (ch))
X || (is_blank && ISBLANK (ch))
X || (is_cntrl && ISCNTRL (ch))
X || (is_digit && ISDIGIT (ch))
X || (is_graph && ISGRAPH (ch))
X || (is_lower && ISLOWER (ch))
X || (is_print && ISPRINT (ch))
X || (is_punct && ISPUNCT (ch))
X || (is_space && ISSPACE (ch))
X || (is_upper && ISUPPER (ch))
X || (is_xdigit && ISXDIGIT (ch)))
X SET_LIST_BIT (ch);
X }
X had_char_class = true;
X }
X else
X {
X c1++;
X while (c1--)
X PATUNFETCH;
X SET_LIST_BIT ('[');
X SET_LIST_BIT (':');
X had_char_class = false;
X }
X }
X else
X {
X had_char_class = false;
X SET_LIST_BIT (c);
X }
X }
X
X /* Discard any (non)matching list bytes that are all 0 at the
X end of the map. Decrease the map-length byte too. */
X while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
X b[-1]--;
X b += b[-1];
X }
X break;
X
X
X case '(':
X if (syntax & RE_NO_BK_PARENS)
X goto handle_open;
X else
X goto normal_char;
X
X
X case ')':
X if (syntax & RE_NO_BK_PARENS)
X goto handle_close;
X else
X goto normal_char;
X
X
X case '\n':
X if (syntax & RE_NEWLINE_ALT)
X goto handle_alt;
X else
X goto normal_char;
X
X
X case '|':
X if (syntax & RE_NO_BK_VBAR)
X goto handle_alt;
X else
X goto normal_char;
X
X
X case '{':
X if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
X goto handle_interval;
X else
X goto normal_char;
X
X
X case '\\':
X if (p == pend) return REG_EESCAPE;
X
X /* Do not translate the character after the \, so that we can
X distinguish, e.g., \B from \b, even if we normally would
X translate, e.g., B to b. */
X PATFETCH_RAW (c);
X
X switch (c)
X {
X case '(':
X if (syntax & RE_NO_BK_PARENS)
X goto normal_backslash;
X
X handle_open:
X bufp->re_nsub++;
X regnum++;
X
X if (COMPILE_STACK_FULL)
X {
X RETALLOC (compile_stack.stack, compile_stack.size << 1,
X compile_stack_elt_t);
X if (compile_stack.stack == NULL) return REG_ESPACE;
X
X compile_stack.size <<= 1;
X }
X
X /* These are the values to restore when we hit end of this
X group. They are all relative offsets, so that if the
X whole pattern moves because of realloc, they will still
X be valid. */
X COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
X COMPILE_STACK_TOP.fixup_alt_jump
X = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
X COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
X COMPILE_STACK_TOP.regnum = regnum;
X
X /* We will eventually replace the 0 with the number of
X groups inner to this one. But do not push a
X start_memory for groups beyond the last one we can
X represent in the compiled pattern. */
X if (regnum <= MAX_REGNUM)
X {
X COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
X BUF_PUSH_3 (start_memory, regnum, 0);
X }
X
X compile_stack.avail++;
X
X fixup_alt_jump = 0;
X laststart = 0;
X begalt = b;
X break;
X
X
X case ')':
X if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
X
X if (COMPILE_STACK_EMPTY)
X if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
X goto normal_backslash;
X else
X return REG_ERPAREN;
X
X handle_close:
X if (fixup_alt_jump)
X { /* Push a dummy failure point at the end of the
X alternative for a possible future
X `pop_failure_jump' to pop. See comments at
X `push_dummy_failure' in `re_match_2'. */
X BUF_PUSH (push_dummy_failure);
X
X /* We allocated space for this jump when we assigned
X to `fixup_alt_jump', in the `handle_alt' case below. */
X STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
X }
X
X /* See similar code for backslashed left paren above. */
X if (COMPILE_STACK_EMPTY)
X if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
X goto normal_char;
X else
X return REG_ERPAREN;
X
X /* Since we just checked for an empty stack above, this
X ``can't happen''. */
X assert (compile_stack.avail != 0);
X {
X /* We don't just want to restore into `regnum', because
X later groups should continue to be numbered higher,
X as in `(ab)c(de)' -- the second group is #2. */
X regnum_t this_group_regnum;
X
X compile_stack.avail--;
X begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
X fixup_alt_jump
X = COMPILE_STACK_TOP.fixup_alt_jump
X ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1
X : 0;
X laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
X this_group_regnum = COMPILE_STACK_TOP.regnum;
X
X /* We're at the end of the group, so now we know how many
X groups were inside this one. */
X if (this_group_regnum <= MAX_REGNUM)
X {
X unsigned char *inner_group_loc
X = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
X
X *inner_group_loc = regnum - this_group_regnum;
X BUF_PUSH_3 (stop_memory, this_group_regnum,
X regnum - this_group_regnum);
X }
X }
X break;
X
X
X case '|': /* `\|'. */
X if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
X goto normal_backslash;
X handle_alt:
X if (syntax & RE_LIMITED_OPS)
X goto normal_char;
X
X /* Insert before the previous alternative a jump which
X jumps to this alternative if the former fails. */
X GET_BUFFER_SPACE (3);
X INSERT_JUMP (on_failure_jump, begalt, b + 6);
X pending_exact = 0;
X b += 3;
X
X /* The alternative before this one has a jump after it
X which gets executed if it gets matched. Adjust that
X jump so it will jump to this alternative's analogous
X jump (put in below, which in turn will jump to the next
X (if any) alternative's such jump, etc.). The last such
X jump jumps to the correct final destination. A picture:
X _____ _____
X | | | |
X | v | v
X a | b | c
X
X If we are at `b', then fixup_alt_jump right now points to a
X three-byte space after `a'. We'll put in the jump, set
X fixup_alt_jump to right after `b', and leave behind three
X bytes which we'll fill in when we get to after `c'. */
X
X if (fixup_alt_jump)
X STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
X
X /* Mark and leave space for a jump after this alternative,
X to be filled in later either by next alternative or
X when know we're at the end of a series of alternatives. */
X fixup_alt_jump = b;
X GET_BUFFER_SPACE (3);
X b += 3;
X
X laststart = 0;
X begalt = b;
X break;
X
X
X case '{':
X /* If \{ is a literal. */
X if (!(syntax & RE_INTERVALS)
X /* If we're at `\{' and it's not the open-interval
X operator. */
X || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
X || (p - 2 == pattern && p == pend))
X goto normal_backslash;
X
X handle_interval:
X {
X /* If got here, then the syntax allows intervals. */
X
X /* At least (most) this many matches must be made. */
X int lower_bound = -1, upper_bound = -1;
X
X beg_interval = p - 1;
X
X if (p == pend)
X {
X if (syntax & RE_NO_BK_BRACES)
X goto unfetch_interval;
X else
X return REG_EBRACE;
X }
X
X GET_UNSIGNED_NUMBER (lower_bound);
X
X if (c == ',')
X {
X GET_UNSIGNED_NUMBER (upper_bound);
X if (upper_bound < 0) upper_bound = RE_DUP_MAX;
X }
X else
X /* Interval such as `{1}' => match exactly once. */
X upper_bound = lower_bound;
X
X if (lower_bound < 0 || upper_bound > RE_DUP_MAX
X || lower_bound > upper_bound)
X {
X if (syntax & RE_NO_BK_BRACES)
X goto unfetch_interval;
X else
X return REG_BADBR;
X }
X
X if (!(syntax & RE_NO_BK_BRACES))
X {
X if (c != '\\') return REG_EBRACE;
X
X PATFETCH (c);
X }
X
X if (c != '}')
X {
X if (syntax & RE_NO_BK_BRACES)
X goto unfetch_interval;
X else
X return REG_BADBR;
X }
X
X /* We just parsed a valid interval. */
X
X /* If it's invalid to have no preceding re. */
X if (!laststart)
X {
X if (syntax & RE_CONTEXT_INVALID_OPS)
X return REG_BADRPT;
X else if (syntax & RE_CONTEXT_INDEP_OPS)
X laststart = b;
X else
X goto unfetch_interval;
X }
X
X /* If the upper bound is zero, don't want to succeed at
X all; jump from `laststart' to `b + 3', which will be
X the end of the buffer after we insert the jump. */
X if (upper_bound == 0)
X {
X GET_BUFFER_SPACE (3);
X INSERT_JUMP (jump, laststart, b + 3);
X b += 3;
X }
X
X /* Otherwise, we have a nontrivial interval. When
X we're all done, the pattern will look like:
X set_number_at <jump count> <upper bound>
X set_number_at <succeed_n count> <lower bound>
X succeed_n <after jump addr> <succed_n count>
X <body of loop>
X jump_n <succeed_n addr> <jump count>
X (The upper bound and `jump_n' are omitted if
X `upper_bound' is 1, though.) */
X else
X { /* If the upper bound is > 1, we need to insert
X more at the end of the loop. */
X unsigned nbytes = 10 + (upper_bound > 1) * 10;
X
X GET_BUFFER_SPACE (nbytes);
X
X /* Initialize lower bound of the `succeed_n', even
X though it will be set during matching by its
X attendant `set_number_at' (inserted next),
X because `re_compile_fastmap' needs to know.
X Jump to the `jump_n' we might insert below. */
X INSERT_JUMP2 (succeed_n, laststart,
X b + 5 + (upper_bound > 1) * 5,
X lower_bound);
X b += 5;
X
X /* Code to initialize the lower bound. Insert
X before the `succeed_n'. The `5' is the last two
X bytes of this `set_number_at', plus 3 bytes of
X the following `succeed_n'. */
X insert_op2 (set_number_at, laststart, 5, lower_bound, b);
X b += 5;
X
X if (upper_bound > 1)
X { /* More than one repetition is allowed, so
X append a backward jump to the `succeed_n'
X that starts this interval.
X
X When we've reached this during matching,
X we'll have matched the interval once, so
X jump back only `upper_bound - 1' times. */
X STORE_JUMP2 (jump_n, b, laststart + 5,
X upper_bound - 1);
X b += 5;
X
X /* The location we want to set is the second
X parameter of the `jump_n'; that is `b-2' as
X an absolute address. `laststart' will be
X the `set_number_at' we're about to insert;
X `laststart+3' the number to set, the source
X for the relative address. But we are
X inserting into the middle of the pattern --
X so everything is getting moved up by 5.
X Conclusion: (b - 2) - (laststart + 3) + 5,
X i.e., b - laststart.
X
X We insert this at the beginning of the loop
X so that if we fail during matching, we'll
X reinitialize the bounds. */
X insert_op2 (set_number_at, laststart, b - laststart,
X upper_bound - 1, b);
X b += 5;
X }
X }
X pending_exact = 0;
X beg_interval = NULL;
X }
X break;
X
X unfetch_interval:
X /* If an invalid interval, match the characters as literals. */
X assert (beg_interval);
X p = beg_interval;
X beg_interval = NULL;
X
X /* normal_char and normal_backslash need `c'. */
X PATFETCH (c);
X
X if (!(syntax & RE_NO_BK_BRACES))
X {
X if (p > pattern && p[-1] == '\\')
X goto normal_backslash;
X }
X goto normal_char;
X
X#ifdef emacs
X /* There is no way to specify the before_dot and after_dot
X operators. rms says this is ok. --karl */
X case '=':
X BUF_PUSH (at_dot);
X break;
X
X case 's':
X laststart = b;
X PATFETCH (c);
X BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
X break;
X
X case 'S':
X laststart = b;
X PATFETCH (c);
X BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
X break;
X#endif /* emacs */
X
X
X case 'w':
X laststart = b;
X BUF_PUSH (wordchar);
X break;
X
X
X case 'W':
X laststart = b;
X BUF_PUSH (notwordchar);
X break;
X
X
X case '<':
X BUF_PUSH (wordbeg);
X break;
X
X case '>':
X BUF_PUSH (wordend);
X break;
X
X case 'b':
X BUF_PUSH (wordbound);
X break;
X
X case 'B':
X BUF_PUSH (notwordbound);
X break;
X
X case '`':
X BUF_PUSH (begbuf);
X break;
X
X case '\'':
X BUF_PUSH (endbuf);
X break;
X
X case '1': case '2': case '3': case '4': case '5':
X case '6': case '7': case '8': case '9':
X if (syntax & RE_NO_BK_REFS)
X goto normal_char;
X
X c1 = c - '0';
X
X if (c1 > regnum)
X return REG_ESUBREG;
X
X /* Can't back reference to a subexpression if inside of it. */
X if (group_in_compile_stack (compile_stack, c1))
X goto normal_char;
X
X laststart = b;
X BUF_PUSH_2 (duplicate, c1);
X break;
X
X
X case '+':
X case '?':
X if (syntax & RE_BK_PLUS_QM)
X goto handle_plus;
X else
X goto normal_backslash;
X
X default:
X normal_backslash:
X /* You might think it would be useful for \ to mean
X not to translate; but if we don't translate it
X it will never match anything. */
X c = TRANSLATE (c);
X goto normal_char;
X }
X break;
X
X
X default:
X /* Expects the character in `c'. */
X normal_char:
X /* If no exactn currently being built. */
X if (!pending_exact
X
X /* If last exactn not at current position. */
X || pending_exact + *pending_exact + 1 != b
X
X /* We have only one byte following the exactn for the count. */
X || *pending_exact == (1 << BYTEWIDTH) - 1
X
X /* If followed by a repetition operator. */
X || *p == '*' || *p == '^'
X || ((syntax & RE_BK_PLUS_QM)
X ? *p == '\\' && (p[1] == '+' || p[1] == '?')
X : (*p == '+' || *p == '?'))
X || ((syntax & RE_INTERVALS)
X && ((syntax & RE_NO_BK_BRACES)
X ? *p == '{'
X : (p[0] == '\\' && p[1] == '{'))))
X {
X /* Start building a new exactn. */
X
X laststart = b;
X
X BUF_PUSH_2 (exactn, 0);
X pending_exact = b - 1;
X }
X
X BUF_PUSH (c);
X (*pending_exact)++;
X break;
X } /* switch (c) */
X } /* while p != pend */
X
X
X /* Through the pattern now. */
X
X if (fixup_alt_jump)
X STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
X
X if (!COMPILE_STACK_EMPTY)
X return REG_EPAREN;
X
X free (compile_stack.stack);
X
X /* We have succeeded; set the length of the buffer. */
X bufp->used = b - bufp->buffer;
X
X#ifdef DEBUG
X if (debug)
X {
X DEBUG_PRINT1 ("\nCompiled pattern: ");
X print_compiled_pattern (bufp);
X }
X#endif /* DEBUG */
X
X return REG_NOERROR;
X} /* regex_compile */
X
X/* Subroutines for `regex_compile'. */
X
X/* Store OP at LOC followed by two-byte integer parameter ARG. */
X
Xstatic void
Xstore_op1 (op, loc, arg)
X re_opcode_t op;
X unsigned char *loc;
X int arg;
X{
X *loc = (unsigned char) op;
X STORE_NUMBER (loc + 1, arg);
X}
X
X
X/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */
X
Xstatic void
Xstore_op2 (op, loc, arg1, arg2)
X re_opcode_t op;
X unsigned char *loc;
X int arg1, arg2;
X{
X *loc = (unsigned char) op;
X STORE_NUMBER (loc + 1, arg1);
X STORE_NUMBER (loc + 3, arg2);
X}
X
X
X/* Copy the bytes from LOC to END to open up three bytes of space at LOC
X for OP followed by two-byte integer parameter ARG. */
X
Xstatic void
Xinsert_op1 (op, loc, arg, end)
X re_opcode_t op;
X unsigned char *loc;
X int arg;
X unsigned char *end;
X{
X register unsigned char *pfrom = end;
X register unsigned char *pto = end + 3;
X
X while (pfrom != loc)
X *--pto = *--pfrom;
X
X store_op1 (op, loc, arg);
X}
X
X
X/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */
X
Xstatic void
Xinsert_op2 (op, loc, arg1, arg2, end)
X re_opcode_t op;
X unsigned char *loc;
X int arg1, arg2;
X unsigned char *end;
X{
X register unsigned char *pfrom = end;
X register unsigned char *pto = end + 5;
X
X while (pfrom != loc)
X *--pto = *--pfrom;
X
X store_op2 (op, loc, arg1, arg2);
X}
X
X
X/* P points to just after a ^ in PATTERN. Return true if that ^ comes
X after an alternative or a begin-subexpression. We assume there is at
X least one character before the ^. */
X
Xstatic boolean
Xat_begline_loc_p (pattern, p, syntax)
X const char *pattern, *p;
X reg_syntax_t syntax;
X{
X const char *prev = p - 2;
X boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
X
X return
X /* After a subexpression? */
X (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
X /* After an alternative? */
X || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
X}
X
X
X/* The dual of at_begline_loc_p. This one is for $. We assume there is
X at least one character after the $, i.e., `P < PEND'. */
X
Xstatic boolean
Xat_endline_loc_p (p, pend, syntax)
X const char *p, *pend;
X int syntax;
X{
X const char *next = p;
X boolean next_backslash = *next == '\\';
X const char *next_next = p + 1 < pend ? p + 1 : NULL;
X
X return
X /* Before a subexpression? */
X (syntax & RE_NO_BK_PARENS ? *next == ')'
X : next_backslash && next_next && *next_next == ')')
X /* Before an alternative? */
X || (syntax & RE_NO_BK_VBAR ? *next == '|'
X : next_backslash && next_next && *next_next == '|');
X}
X
X
X/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
X false if it's not. */
X
Xstatic boolean
Xgroup_in_compile_stack (compile_stack, regnum)
X compile_stack_type compile_stack;
X regnum_t regnum;
X{
X int this_element;
X
X for (this_element = compile_stack.avail - 1;
X this_element >= 0;
X this_element--)
X if (compile_stack.stack[this_element].regnum == regnum)
X return true;
X
X return false;
X}
X
X
X/* Read the ending character of a range (in a bracket expression) from the
X uncompiled pattern *P_PTR (which ends at PEND). We assume the
X starting character is in `P[-2]'. (`P[-1]' is the character `-'.)
X Then we set the translation of all bits between the starting and
X ending characters (inclusive) in the compiled pattern B.
X
X Return an error code.
X
X We use these short variable names so we can use the same macros as
X `regex_compile' itself. */
X
Xstatic reg_errcode_t
Xcompile_range (p_ptr, pend, translate, syntax, b)
X const char **p_ptr, *pend;
X char *translate;
X reg_syntax_t syntax;
X unsigned char *b;
X{
X unsigned this_char;
X
X const char *p = *p_ptr;
X int range_start, range_end;
X
X if (p == pend)
X return REG_ERANGE;
X
X /* Even though the pattern is a signed `char *', we need to fetch
X with unsigned char *'s; if the high bit of the pattern character
X is set, the range endpoints will be negative if we fetch using a
X signed char *.
X
X We also want to fetch the endpoints without translating them; the
X appropriate translation is done in the bit-setting loop below. */
X range_start = ((unsigned char *) p)[-2];
X range_end = ((unsigned char *) p)[0];
X
X /* Have to increment the pointer into the pattern string, so the
X caller isn't still at the ending character. */
X (*p_ptr)++;
X
X /* If the start is after the end, the range is empty. */
X if (range_start > range_end)
X return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
X
X /* Here we see why `this_char' has to be larger than an `unsigned
X char' -- the range is inclusive, so if `range_end' == 0xff
X (assuming 8-bit characters), we would otherwise go into an infinite
X loop, since all characters <= 0xff. */
X for (this_char = range_start; this_char <= range_end; this_char++)
X {
X SET_LIST_BIT (TRANSLATE (this_char));
X }
X
X return REG_NOERROR;
X}
X
X/* Failure stack declarations and macros; both re_compile_fastmap and
X re_match_2 use a failure stack. These have to be macros because of
X REGEX_ALLOCATE. */
X
X
X/* Number of failure points for which to initially allocate space
X when matching. If this number is exceeded, we allocate more
X space, so it is not a hard limit. */
X#ifndef INIT_FAILURE_ALLOC
X#define INIT_FAILURE_ALLOC 5
X#endif
X
X/* Roughly the maximum number of failure points on the stack. Would be
X exactly that if always used MAX_FAILURE_SPACE each time we failed.
X This is a variable only so users of regex can assign to it; we never
X change it ourselves. */
Xint re_max_failures = 2000;
X
Xtypedef const unsigned char *fail_stack_elt_t;
X
Xtypedef struct
X{
X fail_stack_elt_t *stack;
X unsigned size;
X unsigned avail; /* Offset of next open position. */
X} fail_stack_type;
X
X#define FAIL_STACK_EMPTY() (fail_stack.avail == 0)
X#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
X#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size)
X#define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail])
X
X
X/* Initialize `fail_stack'. Do `return -2' if the alloc fails. */
X
X#define INIT_FAIL_STACK() \
X do { \
X fail_stack.stack = (fail_stack_elt_t *) \
X REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
X \
X if (fail_stack.stack == NULL) \
X return -2; \
X \
X fail_stack.size = INIT_FAILURE_ALLOC; \
X fail_stack.avail = 0; \
X } while (0)
X
X
X/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
X
X Return 1 if succeeds, and 0 if either ran out of memory
X allocating space for it or it was already too large.
X
X REGEX_REALLOCATE requires `destination' be declared. */
X
X#define DOUBLE_FAIL_STACK(fail_stack) \
X ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \
X ? 0 \
X : ((fail_stack).stack = (fail_stack_elt_t *) \
X REGEX_REALLOCATE ((fail_stack).stack, \
X (fail_stack).size * sizeof (fail_stack_elt_t), \
X ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \
X \
X (fail_stack).stack == NULL \
X ? 0 \
X : ((fail_stack).size <<= 1, \
X 1)))
X
X
X/* Push PATTERN_OP on FAIL_STACK.
X
X Return 1 if was able to do so and 0 if ran out of memory allocating
X space to do so. */
X#define PUSH_PATTERN_OP(pattern_op, fail_stack) \
X ((FAIL_STACK_FULL () \
X && !DOUBLE_FAIL_STACK (fail_stack)) \
X ? 0 \
X : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \
X 1))
X
X/* This pushes an item onto the failure stack. Must be a four-byte
X value. Assumes the variable `fail_stack'. Probably should only
X be called from within `PUSH_FAILURE_POINT'. */
X#define PUSH_FAILURE_ITEM(item) \
X fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
X
X/* The complement operation. Assumes `fail_stack' is nonempty. */
X#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
X
X/* Used to omit pushing failure point id's when we're not debugging. */
X#ifdef DEBUG
X#define DEBUG_PUSH PUSH_FAILURE_ITEM
X#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM ()
X#else
X#define DEBUG_PUSH(item)
X#define DEBUG_POP(item_addr)
X#endif
X
X
X/* Push the information about the state we will need
X if we ever fail back to it.
X
X Requires variables fail_stack, regstart, regend, reg_info, and
X num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be
X declared.
X
X Does `return FAILURE_CODE' if runs out of memory. */
X
X#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \
X do { \
X char *destination; \
X /* Must be int, so when we don't save any registers, the arithmetic \
X of 0 + -1 isn't done as unsigned. */ \
X int this_reg; \
X \
X DEBUG_STATEMENT (failure_id++); \
X DEBUG_STATEMENT (nfailure_points_pushed++); \
X DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \
X DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\
X DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\
X \
X DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \
X DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \
X \
X /* Ensure we have enough space allocated for what we will push. */ \
X while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \
X { \
X if (!DOUBLE_FAIL_STACK (fail_stack)) \
X return failure_code; \
X \
X DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \
X (fail_stack).size); \
X DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\
X } \
X \
X /* Push the info, starting with the registers. */ \
X DEBUG_PRINT1 ("\n"); \
X \
X for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
X this_reg++) \
X { \
X DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \
X DEBUG_STATEMENT (num_regs_pushed++); \
X \
X DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
X PUSH_FAILURE_ITEM (regstart[this_reg]); \
X \
X DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
X PUSH_FAILURE_ITEM (regend[this_reg]); \
X \
X DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \
X DEBUG_PRINT2 (" match_null=%d", \
X REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \
X DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \
X DEBUG_PRINT2 (" matched_something=%d", \
X MATCHED_SOMETHING (reg_info[this_reg])); \
X DEBUG_PRINT2 (" ever_matched=%d", \
X EVER_MATCHED_SOMETHING (reg_info[this_reg])); \
X DEBUG_PRINT1 ("\n"); \
X PUSH_FAILURE_ITEM (reg_info[this_reg].word); \
X } \
X \
X DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\
X PUSH_FAILURE_ITEM (lowest_active_reg); \
X \
X DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\
X PUSH_FAILURE_ITEM (highest_active_reg); \
X \
X DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \
X DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \
X PUSH_FAILURE_ITEM (pattern_place); \
X \
X DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \
X DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \
X size2); \
X DEBUG_PRINT1 ("'\n"); \
X PUSH_FAILURE_ITEM (string_place); \
X \
X DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \
X DEBUG_PUSH (failure_id); \
X } while (0)
X
X/* This is the number of items that are pushed and popped on the stack
X for each register. */
X#define NUM_REG_ITEMS 3
X
X/* Individual items aside from the registers. */
X#ifdef DEBUG
X#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */
X#else
X#define NUM_NONREG_ITEMS 4
X#endif
X
X/* We push at most this many items on the stack. */
X#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
X
X/* We actually push this many items. */
X#define NUM_FAILURE_ITEMS \
X ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \
X + NUM_NONREG_ITEMS)
X
X/* How many items can still be added to the stack without overflowing it. */
X#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
X
X
X/* Pops what PUSH_FAIL_STACK pushes.
X
X We restore into the parameters, all of which should be lvalues:
X STR -- the saved data position.
X PAT -- the saved pattern position.
X LOW_REG, HIGH_REG -- the highest and lowest active registers.
X REGSTART, REGEND -- arrays of string positions.
X REG_INFO -- array of information about each subexpression.
X
X Also assumes the variables `fail_stack' and (if debugging), `bufp',
X `pend', `string1', `size1', `string2', and `size2'. */
X
X#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
X{ \
X DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \
X int this_reg; \
X const unsigned char *string_temp; \
X \
X assert (!FAIL_STACK_EMPTY ()); \
X \
X /* Remove failure points and point to how many regs pushed. */ \
X DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \
X DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \
X DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \
X \
X assert (fail_stack.avail >= NUM_NONREG_ITEMS); \
X \
X DEBUG_POP (&failure_id); \
X DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \
X \
X /* If the saved string location is NULL, it came from an \
X on_failure_keep_string_jump opcode, and we want to throw away the \
X saved NULL, thus retaining our current position in the string. */ \
X string_temp = POP_FAILURE_ITEM (); \
X if (string_temp != NULL) \
X str = (const char *) string_temp; \
X \
X DEBUG_PRINT2 (" Popping string 0x%x: `", str); \
X DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \
X DEBUG_PRINT1 ("'\n"); \
X \
X pat = (unsigned char *) POP_FAILURE_ITEM (); \
X DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \
X DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \
X \
X /* Restore register info. */ \
X high_reg = (unsigned) POP_FAILURE_ITEM (); \
X DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \
X \
X low_reg = (unsigned) POP_FAILURE_ITEM (); \
X DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \
X \
X for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \
X { \
X DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \
X \
X reg_info[this_reg].word = POP_FAILURE_ITEM (); \
X DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \
X \
X regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \
X DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
X \
X regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \
X DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
X } \
X \
X DEBUG_STATEMENT (nfailure_points_popped++); \
X} /* POP_FAILURE_POINT */
X
X/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
X BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible
X characters can start a string that matches the pattern. This fastmap
X is used by re_search to skip quickly over impossible starting points.
X
X The caller must supply the address of a (1 << BYTEWIDTH)-byte data
X area as BUFP->fastmap.
X
X We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
X the pattern buffer.
X
X Returns 0 if we succeed, -2 if an internal error. */
X
Xint
Xre_compile_fastmap (bufp)
X struct re_pattern_buffer *bufp;
X{
X int j, k;
X fail_stack_type fail_stack;
X#ifndef REGEX_MALLOC
X char *destination;
X#endif
X /* We don't push any register information onto the failure stack. */
X unsigned num_regs = 0;
X
X register char *fastmap = bufp->fastmap;
X unsigned char *pattern = bufp->buffer;
X unsigned long size = bufp->used;
X const unsigned char *p = pattern;
X register unsigned char *pend = pattern + size;
X
X /* Assume that each path through the pattern can be null until
X proven otherwise. We set this false at the bottom of switch
X statement, to which we get only if a particular path doesn't
X match the empty string. */
X boolean path_can_be_null = true;
X
X /* We aren't doing a `succeed_n' to begin with. */
X boolean succeed_n_p = false;
X
X assert (fastmap != NULL && p != NULL);
X
X INIT_FAIL_STACK ();
X bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */
X bufp->fastmap_accurate = 1; /* It will be when we're done. */
X bufp->can_be_null = 0;
X
X while (p != pend || !FAIL_STACK_EMPTY ())
X {
X if (p == pend)
X {
X bufp->can_be_null |= path_can_be_null;
X
X /* Reset for next path. */
X path_can_be_null = true;
X
X p = fail_stack.stack[--fail_stack.avail];
X }
X
X /* We should never be about to go beyond the end of the pattern. */
X assert (p < pend);
X
X#ifdef SWITCH_ENUM_BUG
X switch ((int) ((re_opcode_t) *p++))
X#else
X switch ((re_opcode_t) *p++)
X#endif
X {
X
X /* I guess the idea here is to simply not bother with a fastmap
X if a backreference is used, since it's too hard to figure out
X the fastmap for the corresponding group. Setting
X `can_be_null' stops `re_search_2' from using the fastmap, so
X that is all we do. */
X case duplicate:
X bufp->can_be_null = 1;
X return 0;
X
X
X /* Following are the cases which match a character. These end
X with `break'. */
X
X case exactn:
X fastmap[p[1]] = 1;
X break;
X
X
X case charset:
X for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
X if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
X fastmap[j] = 1;
X break;
X
X
X case charset_not:
X /* Chars beyond end of map must be allowed. */
X for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
X fastmap[j] = 1;
X
X for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
X if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
X fastmap[j] = 1;
X break;
X
X
X case wordchar:
X for (j = 0; j < (1 << BYTEWIDTH); j++)
X if (SYNTAX (j) == Sword)
X fastmap[j] = 1;
X break;
X
X
X case notwordchar:
X for (j = 0; j < (1 << BYTEWIDTH); j++)
X if (SYNTAX (j) != Sword)
X fastmap[j] = 1;
X break;
X
X
X case anychar:
X /* `.' matches anything ... */
X for (j = 0; j < (1 << BYTEWIDTH); j++)
X fastmap[j] = 1;
X
X /* ... except perhaps newline. */
X if (!(bufp->syntax & RE_DOT_NEWLINE))
X fastmap['\n'] = 0;
X
X /* Return if we have already set `can_be_null'; if we have,
X then the fastmap is irrelevant. Something's wrong here. */
X else if (bufp->can_be_null)
X return 0;
X
X /* Otherwise, have to check alternative paths. */
X break;
X
X
X#ifdef emacs
X case syntaxspec:
X k = *p++;
X for (j = 0; j < (1 << BYTEWIDTH); j++)
X if (SYNTAX (j) == (enum syntaxcode) k)
X fastmap[j] = 1;
X break;
X
X
X case notsyntaxspec:
X k = *p++;
X for (j = 0; j < (1 << BYTEWIDTH); j++)
X if (SYNTAX (j) != (enum syntaxcode) k)
X fastmap[j] = 1;
X break;
X
X
X /* All cases after this match the empty string. These end with
X `continue'. */
X
X
X case before_dot:
X case at_dot:
X case after_dot:
X continue;
X#endif /* not emacs */
X
X
X case no_op:
X case begline:
X case endline:
X case begbuf:
X case endbuf:
X case wordbound:
X case notwordbound:
X case wordbeg:
X case wordend:
X case push_dummy_failure:
X continue;
X
X
X case jump_n:
X case pop_failure_jump:
X case maybe_pop_jump:
X case jump:
X case jump_past_alt:
X case dummy_failure_jump:
X EXTRACT_NUMBER_AND_INCR (j, p);
X p += j;
X if (j > 0)
X continue;
X
X /* Jump backward implies we just went through the body of a
X loop and matched nothing. Opcode jumped to should be
X `on_failure_jump' or `succeed_n'. Just treat it like an
X ordinary jump. For a * loop, it has pushed its failure
X point already; if so, discard that as redundant. */
X if ((re_opcode_t) *p != on_failure_jump
X && (re_opcode_t) *p != succeed_n)
X continue;
X
X p++;
X EXTRACT_NUMBER_AND_INCR (j, p);
X p += j;
X
X /* If what's on the stack is where we are now, pop it. */
X if (!FAIL_STACK_EMPTY ()
X && fail_stack.stack[fail_stack.avail - 1] == p)
X fail_stack.avail--;
X
X continue;
X
X
X case on_failure_jump:
X case on_failure_keep_string_jump:
X handle_on_failure_jump:
X EXTRACT_NUMBER_AND_INCR (j, p);
X
X /* For some patterns, e.g., `(a?)?', `p+j' here points to the
X end of the pattern. We don't want to push such a point,
X since when we restore it above, entering the switch will
X increment `p' past the end of the pattern. We don't need
X to push such a point since we obviously won't find any more
X fastmap entries beyond `pend'. Such a pattern can match
X the null string, though. */
X if (p + j < pend)
X {
X if (!PUSH_PATTERN_OP (p + j, fail_stack))
X return -2;
X }
X else
X bufp->can_be_null = 1;
X
X if (succeed_n_p)
X {
X EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */
X succeed_n_p = false;
X }
X
X continue;
X
X
X case succeed_n:
X /* Get to the number of times to succeed. */
X p += 2;
X
X /* Increment p past the n for when k != 0. */
X EXTRACT_NUMBER_AND_INCR (k, p);
X if (k == 0)
X {
X p -= 4;
X succeed_n_p = true; /* Spaghetti code alert. */
X goto handle_on_failure_jump;
X }
X continue;
X
X
X case set_number_at:
X p += 4;
X continue;
X
X
X case start_memory:
X case stop_memory:
X p += 2;
X continue;
X
X
X default:
X abort (); /* We have listed all the cases. */
X } /* switch *p++ */
X
X /* Getting here means we have found the possible starting
X characters for one path of the pattern -- and that the empty
X string does not match. We need not follow this path further.
X Instead, look at the next alternative (remembered on the
X stack), or quit if no more. The test at the top of the loop
X does these things. */
X path_can_be_null = false;
X p = pend;
X } /* while p */
X
X /* Set `can_be_null' for the last path (also the first path, if the
X pattern is empty). */
X bufp->can_be_null |= path_can_be_null;
X return 0;
X} /* re_compile_fastmap */
X
X/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
X ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
X this memory for recording register information. STARTS and ENDS
X must be allocated using the malloc library routine, and must each
X be at least NUM_REGS * sizeof (regoff_t) bytes long.
X
X If NUM_REGS == 0, then subsequent matches should allocate their own
X register data.
X
X Unless this function is called, the first search or match using
X PATTERN_BUFFER will allocate its own register data, without
X freeing the old data. */
X
Xvoid
Xre_set_registers (bufp, regs, num_regs, starts, ends)
X struct re_pattern_buffer *bufp;
X struct re_registers *regs;
X unsigned num_regs;
X regoff_t *starts, *ends;
X{
X if (num_regs)
X {
X bufp->regs_allocated = REGS_REALLOCATE;
X regs->num_regs = num_regs;
X regs->start = starts;
X regs->end = ends;
X }
X else
X {
X bufp->regs_allocated = REGS_UNALLOCATED;
X regs->num_regs = 0;
X regs->start = regs->end = (regoff_t) 0;
X }
X}
X
X/* Searching routines. */
X
X/* Like re_search_2, below, but only one string is specified, and
X doesn't let you say where to stop matching. */
X
Xint
Xre_search (bufp, string, size, startpos, range, regs)
X struct re_pattern_buffer *bufp;
X const char *string;
X int size, startpos, range;
X struct re_registers *regs;
X{
X return re_search_2 (bufp, NULL, 0, string, size, startpos, range,
X regs, size);
X}
X
X
X/* Using the compiled pattern in BUFP->buffer, first tries to match the
X virtual concatenation of STRING1 and STRING2, starting first at index
X STARTPOS, then at STARTPOS + 1, and so on.
X
X STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
X
X RANGE is how far to scan while trying to match. RANGE = 0 means try
X only at STARTPOS; in general, the last start tried is STARTPOS +
X RANGE.
X
X In REGS, return the indices of the virtual concatenation of STRING1
X and STRING2 that matched the entire BUFP->buffer and its contained
X subexpressions.
X
X Do not consider matching one past the index STOP in the virtual
X concatenation of STRING1 and STRING2.
X
X We return either the position in the strings at which the match was
X found, -1 if no match, or -2 if error (such as failure
X stack overflow). */
X
Xint
Xre_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
X struct re_pattern_buffer *bufp;
X const char *string1, *string2;
X int size1, size2;
X int startpos;
X int range;
X struct re_registers *regs;
X int stop;
X{
X int val;
X register char *fastmap = bufp->fastmap;
X register char *translate = bufp->translate;
X int total_size = size1 + size2;
X int endpos = startpos + range;
X
X /* Check for out-of-range STARTPOS. */
X if (startpos < 0 || startpos > total_size)
X return -1;
X
X /* Fix up RANGE if it might eventually take us outside
X the virtual concatenation of STRING1 and STRING2. */
X if (endpos < -1)
X range = -1 - startpos;
X else if (endpos > total_size)
X range = total_size - startpos;
X
X /* If the search isn't to be a backwards one, don't waste time in a
X search for a pattern that must be anchored. */
X if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0)
X {
X if (startpos > 0)
X return -1;
X else
X range = 1;
X }
X
X /* Update the fastmap now if not correct already. */
X if (fastmap && !bufp->fastmap_accurate)
X if (re_compile_fastmap (bufp) == -2)
X return -2;
X
X /* Loop through the string, looking for a place to start matching. */
X for (;;)
X {
X /* If a fastmap is supplied, skip quickly over characters that
X cannot be the start of a match. If the pattern can match the
X null string, however, we don't need to skip characters; we want
X the first null string. */
X if (fastmap && startpos < total_size && !bufp->can_be_null)
X {
X if (range > 0) /* Searching forwards. */
X {
X register const char *d;
X register int lim = 0;
X int irange = range;
X
X if (startpos < size1 && startpos + range >= size1)
X lim = range - (size1 - startpos);
X
X d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
X
X /* Written out as an if-else to avoid testing `translate'
X inside the loop. */
X if (translate)
X while (range > lim
X && !fastmap[(unsigned char)
X translate[(unsigned char) *d++]])
X range--;
X else
X while (range > lim && !fastmap[(unsigned char) *d++])
X range--;
X
X startpos += irange - range;
X }
X else /* Searching backwards. */
X {
X register char c = (size1 == 0 || startpos >= size1
X ? string2[startpos - size1]
X : string1[startpos]);
X
X if (!fastmap[(unsigned char) TRANSLATE (c)])
X goto advance;
X }
X }
X
X /* If can't match the null string, and that's all we have left, fail. */
X if (range >= 0 && startpos == total_size && fastmap
X && !bufp->can_be_null)
X return -1;
X
X val = re_match_2 (bufp, string1, size1, string2, size2,
X startpos, regs, stop);
X if (val >= 0)
X return startpos;
X
X if (val == -2)
X return -2;
X
X advance:
X if (!range)
X break;
X else if (range > 0)
X {
X range--;
X startpos++;
X }
X else
X {
X range++;
X startpos--;
X }
X }
X return -1;
X} /* re_search_2 */
X
X/* Declarations and macros for re_match_2. */
X
Xstatic int bcmp_translate ();
Xstatic boolean alt_match_null_string_p (),
X common_op_match_null_string_p (),
X group_match_null_string_p ();
X
X/* Structure for per-register (a.k.a. per-group) information.
X This must not be longer than one word, because we push this value
X onto the failure stack. Other register information, such as the
X starting and ending positions (which are addresses), and the list of
X inner groups (which is a bits list) are maintained in separate
X variables.
X
X We are making a (strictly speaking) nonportable assumption here: that
X the compiler will pack our bit fields into something that fits into
X the type of `word', i.e., is something that fits into one item on the
X failure stack. */
Xtypedef union
X{
X fail_stack_elt_t word;
X struct
X {
X /* This field is one if this group can match the empty string,
X zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */
X#define MATCH_NULL_UNSET_VALUE 3
X unsigned match_null_string_p : 2;
X unsigned is_active : 1;
X unsigned matched_something : 1;
X unsigned ever_matched_something : 1;
X } bits;
X} register_info_type;
X
X#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p)
X#define IS_ACTIVE(R) ((R).bits.is_active)
X#define MATCHED_SOMETHING(R) ((R).bits.matched_something)
X#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something)
X
X
X/* Call this when have matched a real character; it sets `matched' flags
X for the subexpressions which we are currently inside. Also records
X that those subexprs have matched. */
X#define SET_REGS_MATCHED() \
X do \
X { \
X unsigned r; \
X for (r = lowest_active_reg; r <= highest_active_reg; r++) \
X { \
X MATCHED_SOMETHING (reg_info[r]) \
X = EVER_MATCHED_SOMETHING (reg_info[r]) \
X = 1; \
X } \
X } \
X while (0)
X
X
X/* This converts PTR, a pointer into one of the search strings `string1'
X and `string2' into an offset from the beginning of that string. */
X#define POINTER_TO_OFFSET(ptr) \
X (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1)
X
X/* Registers are set to a sentinel when they haven't yet matched. */
X#define REG_UNSET_VALUE ((char *) -1)
X#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
X
X
X/* Macros for dealing with the split strings in re_match_2. */
X
X#define MATCHING_IN_FIRST_STRING (dend == end_match_1)
X
X/* Call before fetching a character with *d. This switches over to
X string2 if necessary. */
X#define PREFETCH() \
X while (d == dend) \
X { \
X /* End of string2 => fail. */ \
X if (dend == end_match_2) \
X goto fail; \
X /* End of string1 => advance to string2. */ \
X d = string2; \
X dend = end_match_2; \
X }
X
X
X/* Test if at very beginning or at very end of the virtual concatenation
X of `string1' and `string2'. If only one string, it's `string2'. */
X#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
X#define AT_STRINGS_END(d) ((d) == end2)
X
X
X/* Test if D points to a character which is word-constituent. We have
X two special cases to check for: if past the end of string1, look at
X the first character in string2; and if before the beginning of
X string2, look at the last character in string1. */
X#define WORDCHAR_P(d) \
X (SYNTAX ((d) == end1 ? *string2 \
X : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \
X == Sword)
X
X/* Test if the character before D and the one at D differ with respect
X to being word-constituent. */
X#define AT_WORD_BOUNDARY(d) \
X (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \
X || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
X
X
X/* Free everything we malloc. */
X#ifdef REGEX_MALLOC
X#define FREE_VAR(var) if (var) free (var); var = NULL
X#define FREE_VARIABLES() \
X do { \
X FREE_VAR (fail_stack.stack); \
X FREE_VAR (regstart); \
X FREE_VAR (regend); \
X FREE_VAR (old_regstart); \
X FREE_VAR (old_regend); \
X FREE_VAR (best_regstart); \
X FREE_VAR (best_regend); \
X FREE_VAR (reg_info); \
X FREE_VAR (reg_dummy); \
X FREE_VAR (reg_info_dummy); \
X } while (0)
X#else /* not REGEX_MALLOC */
X/* Some MIPS systems (at least) want this to free alloca'd storage. */
X#define FREE_VARIABLES() alloca (0)
X#endif /* not REGEX_MALLOC */
X
X
X/* These values must meet several constraints. They must not be valid
X register values; since we have a limit of 255 registers (because
X we use only one byte in the pattern for the register number), we can
X use numbers larger than 255. They must differ by 1, because of
X NUM_FAILURE_ITEMS above. And the value for the lowest register must
X be larger than the value for the highest register, so we do not try
X to actually save any registers when none are active. */
X#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
X#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
X
X/* Matching routines. */
X
X#ifndef emacs /* Emacs never uses this. */
X/* re_match is like re_match_2 except it takes only a single string. */
X
Xint
Xre_match (bufp, string, size, pos, regs)
X struct re_pattern_buffer *bufp;
X const char *string;
X int size, pos;
X struct re_registers *regs;
X {
X return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size);
X}
X#endif /* not emacs */
X
X
X/* re_match_2 matches the compiled pattern in BUFP against the
X the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
X and SIZE2, respectively). We start matching at POS, and stop
X matching at STOP.
X
X If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
X store offsets for the substring each group matched in REGS. See the
X documentation for exactly how many groups we fill.
X
X We return -1 if no match, -2 if an internal error (such as the
X failure stack overflowing). Otherwise, we return the length of the
X matched substring. */
X
Xint
Xre_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
X struct re_pattern_buffer *bufp;
X const char *string1, *string2;
X int size1, size2;
X int pos;
X struct re_registers *regs;
X int stop;
X{
X /* General temporaries. */
X int mcnt;
X unsigned char *p1;
X
X /* Just past the end of the corresponding string. */
X const char *end1, *end2;
X
X /* Pointers into string1 and string2, just past the last characters in
X each to consider matching. */
X const char *end_match_1, *end_match_2;
X
X /* Where we are in the data, and the end of the current string. */
X const char *d, *dend;
X
X /* Where we are in the pattern, and the end of the pattern. */
X unsigned char *p = bufp->buffer;
X register unsigned char *pend = p + bufp->used;
X
X /* We use this to map every character in the string. */
X char *translate = bufp->translate;
X
X /* Failure point stack. Each place that can handle a failure further
X down the line pushes a failure point on this stack. It consists of
X restart, regend, and reg_info for all registers corresponding to
X the subexpressions we're currently inside, plus the number of such
X registers, and, finally, two char *'s. The first char * is where
X to resume scanning the pattern; the second one is where to resume
X scanning the strings. If the latter is zero, the failure point is
X a ``dummy''; if a failure happens and the failure point is a dummy,
X it gets discarded and the next next one is tried. */
X fail_stack_type fail_stack;
X#ifdef DEBUG
X static unsigned failure_id = 0;
X unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
X#endif
X
X /* We fill all the registers internally, independent of what we
X return, for use in backreferences. The number here includes
X an element for register zero. */
X unsigned num_regs = bufp->re_nsub + 1;
X
X /* The currently active registers. */
X unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG;
X unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG;
X
X /* Information on the contents of registers. These are pointers into
X the input strings; they record just what was matched (on this
X attempt) by a subexpression part of the pattern, that is, the
X regnum-th regstart pointer points to where in the pattern we began
X matching and the regnum-th regend points to right after where we
X stopped matching the regnum-th subexpression. (The zeroth register
X keeps track of what the whole pattern matches.) */
X const char **regstart, **regend;
X
X /* If a group that's operated upon by a repetition operator fails to
X match anything, then the register for its start will need to be
X restored because it will have been set to wherever in the string we
X are when we last see its open-group operator. Similarly for a
X register's end. */
X const char **old_regstart, **old_regend;
X
X /* The is_active field of reg_info helps us keep track of which (possibly
X nested) subexpressions we are currently in. The matched_something
X field of reg_info[reg_num] helps us tell whether or not we have
X matched any of the pattern so far this time through the reg_num-th
X subexpression. These two fields get reset each time through any
X loop their register is in. */
X register_info_type *reg_info;
X
X /* The following record the register info as found in the above
X variables when we find a match better than any we've seen before.
X This happens as we backtrack through the failure points, which in
X turn happens only if we have not yet matched the entire string. */
X unsigned best_regs_set = false;
X const char **best_regstart, **best_regend;
X
X /* Logically, this is `best_regend[0]'. But we don't want to have to
X allocate space for that if we're not allocating space for anything
X else (see below). Also, we never need info about register 0 for
X any of the other register vectors, and it seems rather a kludge to
X treat `best_regend' differently than the rest. So we keep track of
X the end of the best match so far in a separate variable. We
X initialize this to NULL so that when we backtrack the first time
X and need to test it, it's not garbage. */
X const char *match_end = NULL;
X
X /* Used when we pop values we don't care about. */
X const char **reg_dummy;
X register_info_type *reg_info_dummy;
X
X#ifdef DEBUG
X /* Counts the total number of registers pushed. */
X unsigned num_regs_pushed = 0;
X#endif
X
X DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
X
X INIT_FAIL_STACK ();
X
X /* Do not bother to initialize all the register variables if there are
X no groups in the pattern, as it takes a fair amount of time. If
X there are groups, we include space for register 0 (the whole
X pattern), even though we never use it, since it simplifies the
X array indexing. We should fix this. */
X if (bufp->re_nsub)
X {
X regstart = REGEX_TALLOC (num_regs, const char *);
X regend = REGEX_TALLOC (num_regs, const char *);
X old_regstart = REGEX_TALLOC (num_regs, const char *);
X old_regend = REGEX_TALLOC (num_regs, const char *);
X best_regstart = REGEX_TALLOC (num_regs, const char *);
X best_regend = REGEX_TALLOC (num_regs, const char *);
X reg_info = REGEX_TALLOC (num_regs, register_info_type);
X reg_dummy = REGEX_TALLOC (num_regs, const char *);
X reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type);
X
X if (!(regstart && regend && old_regstart && old_regend && reg_info
X && best_regstart && best_regend && reg_dummy && reg_info_dummy))
X {
X FREE_VARIABLES ();
X return -2;
X }
X }
X#ifdef REGEX_MALLOC
X else
X {
X /* We must initialize all our variables to NULL, so that
X `FREE_VARIABLES' doesn't try to free them. */
X regstart = regend = old_regstart = old_regend = best_regstart
X = best_regend = reg_dummy = NULL;
X reg_info = reg_info_dummy = (register_info_type *) NULL;
X }
X#endif /* REGEX_MALLOC */
X
X /* The starting position is bogus. */
X if (pos < 0 || pos > size1 + size2)
X {
X FREE_VARIABLES ();
X return -1;
X }
X
X /* Initialize subexpression text positions to -1 to mark ones that no
X start_memory/stop_memory has been seen for. Also initialize the
X register information struct. */
X for (mcnt = 1; mcnt < num_regs; mcnt++)
X {
X regstart[mcnt] = regend[mcnt]
X = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
X
X REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
X IS_ACTIVE (reg_info[mcnt]) = 0;
X MATCHED_SOMETHING (reg_info[mcnt]) = 0;
X EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
X }
X
X /* We move `string1' into `string2' if the latter's empty -- but not if
X `string1' is null. */
X if (size2 == 0 && string1 != NULL)
X {
X string2 = string1;
X size2 = size1;
X string1 = 0;
X size1 = 0;
X }
X end1 = string1 + size1;
X end2 = string2 + size2;
X
X /* Compute where to stop matching, within the two strings. */
X if (stop <= size1)
X {
X end_match_1 = string1 + stop;
X end_match_2 = string2;
X }
X else
X {
X end_match_1 = end1;
X end_match_2 = string2 + stop - size1;
X }
X
X /* `p' scans through the pattern as `d' scans through the data.
X `dend' is the end of the input string that `d' points within. `d'
X is advanced into the following input string whenever necessary, but
X this happens before fetching; therefore, at the beginning of the
X loop, `d' can be pointing at the end of a string, but it cannot
X equal `string2'. */
X if (size1 > 0 && pos <= size1)
X {
X d = string1 + pos;
X dend = end_match_1;
X }
X else
X {
X d = string2 + pos - size1;
X dend = end_match_2;
X }
X
X DEBUG_PRINT1 ("The compiled pattern is: ");
X DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
X DEBUG_PRINT1 ("The string to match is: `");
X DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
X DEBUG_PRINT1 ("'\n");
X
X /* This loops over pattern commands. It exits by returning from the
X function if the match is complete, or it drops through if the match
X fails at this starting point in the input data. */
X for (;;)
X {
X DEBUG_PRINT2 ("\n0x%x: ", p);
X
X if (p == pend)
X { /* End of pattern means we might have succeeded. */
X DEBUG_PRINT1 ("end of pattern ... ");
X
X /* If we haven't matched the entire string, and we want the
X longest match, try backtracking. */
X if (d != end_match_2)
X {
X DEBUG_PRINT1 ("backtracking.\n");
X
X if (!FAIL_STACK_EMPTY ())
X { /* More failure points to try. */
X boolean same_str_p = (FIRST_STRING_P (match_end)
X == MATCHING_IN_FIRST_STRING);
X
X /* If exceeds best match so far, save it. */
X if (!best_regs_set
X || (same_str_p && d > match_end)
X || (!same_str_p && !MATCHING_IN_FIRST_STRING))
X {
X best_regs_set = true;
X match_end = d;
X
X DEBUG_PRINT1 ("\nSAVING match as best so far.\n");
X
X for (mcnt = 1; mcnt < num_regs; mcnt++)
X {
X best_regstart[mcnt] = regstart[mcnt];
X best_regend[mcnt] = regend[mcnt];
X }
X }
X goto fail;
X }
X
X /* If no failure points, don't restore garbage. */
X else if (best_regs_set)
X {
X restore_best_regs:
X /* Restore best match. It may happen that `dend ==
X end_match_1' while the restored d is in string2.
X For example, the pattern `x.*y.*z' against the
X strings `x-' and `y-z-', if the two strings are
X not consecutive in memory. */
X DEBUG_PRINT1 ("Restoring best registers.\n");
X
X d = match_end;
X dend = ((d >= string1 && d <= end1)
X ? end_match_1 : end_match_2);
X
X for (mcnt = 1; mcnt < num_regs; mcnt++)
X {
X regstart[mcnt] = best_regstart[mcnt];
X regend[mcnt] = best_regend[mcnt];
X }
X }
X } /* d != end_match_2 */
X
X DEBUG_PRINT1 ("Accepting match.\n");
X
X /* If caller wants register contents data back, do it. */
X if (regs && !bufp->no_sub)
X {
X /* Have the register data arrays been allocated? */
X if (bufp->regs_allocated == REGS_UNALLOCATED)
X { /* No. So allocate them with malloc. We need one
X extra element beyond `num_regs' for the `-1' marker
X GNU code uses. */
X regs->num_regs = MAX (RE_NREGS, num_regs + 1);
X regs->start = TALLOC (regs->num_regs, regoff_t);
X regs->end = TALLOC (regs->num_regs, regoff_t);
X if (regs->start == NULL || regs->end == NULL)
X return -2;
X bufp->regs_allocated = REGS_REALLOCATE;
X }
X else if (bufp->regs_allocated == REGS_REALLOCATE)
X { /* Yes. If we need more elements than were already
X allocated, reallocate them. If we need fewer, just
X leave it alone. */
X if (regs->num_regs < num_regs + 1)
X {
X regs->num_regs = num_regs + 1;
X RETALLOC (regs->start, regs->num_regs, regoff_t);
X RETALLOC (regs->end, regs->num_regs, regoff_t);
X if (regs->start == NULL || regs->end == NULL)
X return -2;
X }
X }
X else
X assert (bufp->regs_allocated == REGS_FIXED);
X
X /* Convert the pointer data in `regstart' and `regend' to
X indices. Register zero has to be set differently,
X since we haven't kept track of any info for it. */
X if (regs->num_regs > 0)
X {
X regs->start[0] = pos;
X regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1
X : d - string2 + size1);
X }
X
X /* Go through the first `min (num_regs, regs->num_regs)'
X registers, since that is all we initialized. */
X for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++)
X {
X if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
X regs->start[mcnt] = regs->end[mcnt] = -1;
X else
X {
X regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]);
X regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]);
X }
X }
X
X /* If the regs structure we return has more elements than
X were in the pattern, set the extra elements to -1. If
X we (re)allocated the registers, this is the case,
X because we always allocate enough to have at least one
X -1 at the end. */
X for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++)
X regs->start[mcnt] = regs->end[mcnt] = -1;
X } /* regs && !bufp->no_sub */
X
X FREE_VARIABLES ();
X DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
X nfailure_points_pushed, nfailure_points_popped,
X nfailure_points_pushed - nfailure_points_popped);
X DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);
X
X mcnt = d - pos - (MATCHING_IN_FIRST_STRING
X ? string1
X : string2 - size1);
X
X DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
X
X return mcnt;
X }
X
X /* Otherwise match next pattern command. */
X#ifdef SWITCH_ENUM_BUG
X switch ((int) ((re_opcode_t) *p++))
X#else
X switch ((re_opcode_t) *p++)
X#endif
X {
X /* Ignore these. Used to ignore the n of succeed_n's which
X currently have n == 0. */
X case no_op:
X DEBUG_PRINT1 ("EXECUTING no_op.\n");
X break;
X
X
X /* Match the next n pattern characters exactly. The following
X byte in the pattern defines n, and the n bytes after that
X are the characters to match. */
X case exactn:
X mcnt = *p++;
X DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
X
X /* This is written out as an if-else so we don't waste time
X testing `translate' inside the loop. */
X if (translate)
X {
X do
X {
X PREFETCH ();
X if (translate[(unsigned char) *d++] != (char) *p++)
X goto fail;
X }
X while (--mcnt);
X }
X else
X {
X do
X {
X PREFETCH ();
X if (*d++ != (char) *p++) goto fail;
X }
X while (--mcnt);
X }
X SET_REGS_MATCHED ();
X break;
X
X
X /* Match any character except possibly a newline or a null. */
X case anychar:
X DEBUG_PRINT1 ("EXECUTING anychar.\n");
X
X PREFETCH ();
X
X if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
X || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
X goto fail;
X
X SET_REGS_MATCHED ();
X DEBUG_PRINT2 (" Matched `%d'.\n", *d);
X d++;
X break;
X
X
X case charset:
X case charset_not:
X {
X register unsigned char c;
X boolean not = (re_opcode_t) *(p - 1) == charset_not;
X
X DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
X
X PREFETCH ();
X c = TRANSLATE (*d); /* The character to match. */
X
X /* Cast to `unsigned' instead of `unsigned char' in case the
X bit list is a full 32 bytes long. */
X if (c < (unsigned) (*p * BYTEWIDTH)
X && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
X not = !not;
X
X p += 1 + *p;
X
X if (!not) goto fail;
X
X SET_REGS_MATCHED ();
X d++;
X break;
X }
X
X
X /* The beginning of a group is represented by start_memory.
X The arguments are the register number in the next byte, and the
X number of groups inner to this one in the next. The text
X matched within the group is recorded (in the internal
X registers data structure) under the register number. */
X case start_memory:
X DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
X
X /* Find out if this group can match the empty string. */
X p1 = p; /* To send to group_match_null_string_p. */
X
X if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
X REG_MATCH_NULL_STRING_P (reg_info[*p])
X = group_match_null_string_p (&p1, pend, reg_info);
X
X /* Save the position in the string where we were the last time
X we were at this open-group operator in case the group is
X operated upon by a repetition operator, e.g., with `(a*)*b'
X against `ab'; then we want to ignore where we are now in
X the string in case this attempt to match fails. */
X old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
X ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
X : regstart[*p];
X DEBUG_PRINT2 (" old_regstart: %d\n",
X POINTER_TO_OFFSET (old_regstart[*p]));
X
X regstart[*p] = d;
X DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
X
X IS_ACTIVE (reg_info[*p]) = 1;
X MATCHED_SOMETHING (reg_info[*p]) = 0;
X
X /* This is the new highest active register. */
X highest_active_reg = *p;
X
X /* If nothing was active before, this is the new lowest active
X register. */
X if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
X lowest_active_reg = *p;
X
X /* Move past the register number and inner group count. */
X p += 2;
X break;
X
X
X /* The stop_memory opcode represents the end of a group. Its
X arguments are the same as start_memory's: the register
X number, and the number of inner groups. */
X case stop_memory:
X DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
X
X /* We need to save the string position the last time we were at
X this close-group operator in case the group is operated
X upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
X against `aba'; then we want to ignore where we are now in
X the string in case this attempt to match fails. */
X old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
X ? REG_UNSET (regend[*p]) ? d : regend[*p]
X : regend[*p];
X DEBUG_PRINT2 (" old_regend: %d\n",
X POINTER_TO_OFFSET (old_regend[*p]));
X
X regend[*p] = d;
X DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
X
X /* This register isn't active anymore. */
X IS_ACTIVE (reg_info[*p]) = 0;
X
X /* If this was the only register active, nothing is active
X anymore. */
X if (lowest_active_reg == highest_active_reg)
X {
X lowest_active_reg = NO_LOWEST_ACTIVE_REG;
X highest_active_reg = NO_HIGHEST_ACTIVE_REG;
X }
X else
X { /* We must scan for the new highest active register, since
X it isn't necessarily one less than now: consider
X (a(b)c(d(e)f)g). When group 3 ends, after the f), the
X new highest active register is 1. */
X unsigned char r = *p - 1;
X while (r > 0 && !IS_ACTIVE (reg_info[r]))
X r--;
X
X /* If we end up at register zero, that means that we saved
X the registers as the result of an `on_failure_jump', not
X a `start_memory', and we jumped to past the innermost
X `stop_memory'. For example, in ((.)*) we save
X registers 1 and 2 as a result of the *, but when we pop
X back to the second ), we are at the stop_memory 1.
X Thus, nothing is active. */
X if (r == 0)
X {
X lowest_active_reg = NO_LOWEST_ACTIVE_REG;
X highest_active_reg = NO_HIGHEST_ACTIVE_REG;
X }
X else
X highest_active_reg = r;
X }
X
X /* If just failed to match something this time around with a
X group that's operated on by a repetition operator, try to
X force exit from the ``loop'', and restore the register
X information for this group that we had before trying this
X last match. */
X if ((!MATCHED_SOMETHING (reg_info[*p])
X || (re_opcode_t) p[-3] == start_memory)
X && (p + 2) < pend)
X {
X boolean is_a_jump_n = false;
X
X p1 = p + 2;
X mcnt = 0;
X switch ((re_opcode_t) *p1++)
X {
X case jump_n:
X is_a_jump_n = true;
X case pop_failure_jump:
X case maybe_pop_jump:
X case jump:
X case dummy_failure_jump:
X EXTRACT_NUMBER_AND_INCR (mcnt, p1);
X if (is_a_jump_n)
X p1 += 2;
X break;
X
X default:
X /* do nothing */ ;
X }
X p1 += mcnt;
X
X /* If the next operation is a jump backwards in the pattern
X to an on_failure_jump right before the start_memory
X corresponding to this stop_memory, exit from the loop
X by forcing a failure after pushing on the stack the
X on_failure_jump's jump in the pattern, and d. */
X if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
X && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
X {
X /* If this group ever matched anything, then restore
X what its registers were before trying this last
X failed match, e.g., with `(a*)*b' against `ab' for
X regstart[1], and, e.g., with `((a*)*(b*)*)*'
X against `aba' for regend[3].
X
X Also restore the registers for inner groups for,
X e.g., `((a*)(b*))*' against `aba' (register 3 would
X otherwise get trashed). */
X
X if (EVER_MATCHED_SOMETHING (reg_info[*p]))
X {
X unsigned r;
X
X EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
X
X /* Restore this and inner groups' (if any) registers. */
X for (r = *p; r < *p + *(p + 1); r++)
X {
X regstart[r] = old_regstart[r];
X
X /* xx why this test? */
X if ((int) old_regend[r] >= (int) regstart[r])
X regend[r] = old_regend[r];
X }
X }
X p1++;
X EXTRACT_NUMBER_AND_INCR (mcnt, p1);
X PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
X
X goto fail;
X }
X }
X
X /* Move past the register number and the inner group count. */
X p += 2;
X break;
X
X
X /* \<digit> has been turned into a `duplicate' command which is
X followed by the numeric value of <digit> as the register number. */
X case duplicate:
X {
X register const char *d2, *dend2;
X int regno = *p++; /* Get which register to match against. */
X DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
X
X /* Can't back reference a group which we've never matched. */
X if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
X goto fail;
X
X /* Where in input to try to start matching. */
X d2 = regstart[regno];
X
X /* Where to stop matching; if both the place to start and
X the place to stop matching are in the same string, then
X set to the place to stop, otherwise, for now have to use
X the end of the first string. */
X
X dend2 = ((FIRST_STRING_P (regstart[regno])
X == FIRST_STRING_P (regend[regno]))
X ? regend[regno] : end_match_1);
X for (;;)
X {
X /* If necessary, advance to next segment in register
X contents. */
X while (d2 == dend2)
X {
X if (dend2 == end_match_2) break;
X if (dend2 == regend[regno]) break;
X
X /* End of string1 => advance to string2. */
X d2 = string2;
X dend2 = regend[regno];
X }
X /* At end of register contents => success */
X if (d2 == dend2) break;
X
X /* If necessary, advance to next segment in data. */
X PREFETCH ();
X
X /* How many characters left in this segment to match. */
X mcnt = dend - d;
X
X /* Want how many consecutive characters we can match in
X one shot, so, if necessary, adjust the count. */
X if (mcnt > dend2 - d2)
X mcnt = dend2 - d2;
X
X /* Compare that many; failure if mismatch, else move
X past them. */
X if (translate
X ? bcmp_translate (d, d2, mcnt, translate)
X : bcmp (d, d2, mcnt))
X goto fail;
X d += mcnt, d2 += mcnt;
X }
X }
X break;
X
X
X /* begline matches the empty string at the beginning of the string
X (unless `not_bol' is set in `bufp'), and, if
X `newline_anchor' is set, after newlines. */
X case begline:
X DEBUG_PRINT1 ("EXECUTING begline.\n");
X
X if (AT_STRINGS_BEG (d))
X {
X if (!bufp->not_bol) break;
X }
X else if (d[-1] == '\n' && bufp->newline_anchor)
X {
X break;
X }
X /* In all other cases, we fail. */
X goto fail;
X
X
X /* endline is the dual of begline. */
X case endline:
X DEBUG_PRINT1 ("EXECUTING endline.\n");
X
X if (AT_STRINGS_END (d))
X {
X if (!bufp->not_eol) break;
X }
X
X /* We have to ``prefetch'' the next character. */
X else if ((d == end1 ? *string2 : *d) == '\n'
X && bufp->newline_anchor)
X {
X break;
X }
X goto fail;
X
X
X /* Match at the very beginning of the data. */
X case begbuf:
X DEBUG_PRINT1 ("EXECUTING begbuf.\n");
X if (AT_STRINGS_BEG (d))
X break;
X goto fail;
X
X
X /* Match at the very end of the data. */
X case endbuf:
X DEBUG_PRINT1 ("EXECUTING endbuf.\n");
X if (AT_STRINGS_END (d))
X break;
X goto fail;
X
X
X /* on_failure_keep_string_jump is used to optimize `.*\n'. It
X pushes NULL as the value for the string on the stack. Then
X `pop_failure_point' will keep the current value for the
X string, instead of restoring it. To see why, consider
X matching `foo\nbar' against `.*\n'. The .* matches the foo;
X then the . fails against the \n. But the next thing we want
X to do is match the \n against the \n; if we restored the
X string value, we would be back at the foo.
X
X Because this is used only in specific cases, we don't need to
X check all the things that `on_failure_jump' does, to make
X sure the right things get saved on the stack. Hence we don't
X share its code. The only reason to push anything on the
X stack at all is that otherwise we would have to change
X `anychar's code to do something besides goto fail in this
X case; that seems worse than this. */
X case on_failure_keep_string_jump:
X DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
X
X EXTRACT_NUMBER_AND_INCR (mcnt, p);
X DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
X
X PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
X break;
X
X
X /* Uses of on_failure_jump:
X
X Each alternative starts with an on_failure_jump that points
X to the beginning of the next alternative. Each alternative
X except the last ends with a jump that in effect jumps past
X the rest of the alternatives. (They really jump to the
X ending jump of the following alternative, because tensioning
X these jumps is a hassle.)
X
X Repeats start with an on_failure_jump that points past both
X the repetition text and either the following jump or
X pop_failure_jump back to this on_failure_jump. */
X case on_failure_jump:
X on_failure:
X DEBUG_PRINT1 ("EXECUTING on_failure_jump");
X
X EXTRACT_NUMBER_AND_INCR (mcnt, p);
X DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
X
X /* If this on_failure_jump comes right before a group (i.e.,
X the original * applied to a group), save the information
X for that group and all inner ones, so that if we fail back
X to this point, the group's information will be correct.
X For example, in \(a*\)*\1, we need the preceding group,
X and in \(\(a*\)b*\)\2, we need the inner group. */
X
X /* We can't use `p' to check ahead because we push
X a failure point to `p + mcnt' after we do this. */
X p1 = p;
X
X /* We need to skip no_op's before we look for the
X start_memory in case this on_failure_jump is happening as
X the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
X against aba. */
X while (p1 < pend && (re_opcode_t) *p1 == no_op)
X p1++;
X
X if (p1 < pend && (re_opcode_t) *p1 == start_memory)
X {
X /* We have a new highest active register now. This will
X get reset at the start_memory we are about to get to,
X but we will have saved all the registers relevant to
X this repetition op, as described above. */
X highest_active_reg = *(p1 + 1) + *(p1 + 2);
X if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
X lowest_active_reg = *(p1 + 1);
X }
X
X DEBUG_PRINT1 (":\n");
X PUSH_FAILURE_POINT (p + mcnt, d, -2);
X break;
X
X
X /* A smart repeat ends with `maybe_pop_jump'.
X We change it to either `pop_failure_jump' or `jump'. */
X case maybe_pop_jump:
X EXTRACT_NUMBER_AND_INCR (mcnt, p);
X DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
X {
X register unsigned char *p2 = p;
X
X /* Compare the beginning of the repeat with what in the
X pattern follows its end. If we can establish that there
X is nothing that they would both match, i.e., that we
X would have to backtrack because of (as in, e.g., `a*a')
X then we can change to pop_failure_jump, because we'll
X never have to backtrack.
X
X This is not true in the case of alternatives: in
X `(a|ab)*' we do need to backtrack to the `ab' alternative
X (e.g., if the string was `ab'). But instead of trying to
X detect that here, the alternative has put on a dummy
X failure point which is what we will end up popping. */
X
X /* Skip over open/close-group commands. */
X while (p2 + 2 < pend
X && ((re_opcode_t) *p2 == stop_memory
X || (re_opcode_t) *p2 == start_memory))
X p2 += 3; /* Skip over args, too. */
X
X /* If we're at the end of the pattern, we can change. */
X if (p2 == pend)
X {
X /* Consider what happens when matching ":\(.*\)"
X against ":/". I don't really understand this code
X yet. */
X p[-3] = (unsigned char) pop_failure_jump;
X DEBUG_PRINT1
X (" End of pattern: change to `pop_failure_jump'.\n");
X }
X
X else if ((re_opcode_t) *p2 == exactn
X || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
X {
X register unsigned char c
X = *p2 == (unsigned char) endline ? '\n' : p2[2];
X p1 = p + mcnt;
X
X /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
X to the `maybe_finalize_jump' of this case. Examine what
X follows. */
X if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
X {
X p[-3] = (unsigned char) pop_failure_jump;
X DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n",
X c, p1[5]);
X }
X
X else if ((re_opcode_t) p1[3] == charset
X || (re_opcode_t) p1[3] == charset_not)
X {
X int not = (re_opcode_t) p1[3] == charset_not;
X
X if (c < (unsigned char) (p1[4] * BYTEWIDTH)
X && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
X not = !not;
X
X /* `not' is equal to 1 if c would match, which means
X that we can't change to pop_failure_jump. */
X if (!not)
X {
X p[-3] = (unsigned char) pop_failure_jump;
X DEBUG_PRINT1 (" No match => pop_failure_jump.\n");
X }
X }
X }
X }
X p -= 2; /* Point at relative address again. */
X if ((re_opcode_t) p[-1] != pop_failure_jump)
X {
X p[-1] = (unsigned char) jump;
X DEBUG_PRINT1 (" Match => jump.\n");
X goto unconditional_jump;
X }
X /* Note fall through. */
X
X
X /* The end of a simple repeat has a pop_failure_jump back to
X its matching on_failure_jump, where the latter will push a
X failure point. The pop_failure_jump takes off failure
X points put on by this pop_failure_jump's matching
X on_failure_jump; we got through the pattern to here from the
X matching on_failure_jump, so didn't fail. */
X case pop_failure_jump:
X {
X /* We need to pass separate storage for the lowest and
X highest registers, even though we don't care about the
X actual values. Otherwise, we will restore only one
X register from the stack, since lowest will == highest in
X `pop_failure_point'. */
X unsigned dummy_low_reg, dummy_high_reg;
X unsigned char *pdummy;
X const char *sdummy;
X
X DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
X POP_FAILURE_POINT (sdummy, pdummy,
X dummy_low_reg, dummy_high_reg,
X reg_dummy, reg_dummy, reg_info_dummy);
X }
X /* Note fall through. */
X
X
X /* Unconditionally jump (without popping any failure points). */
X case jump:
X unconditional_jump:
X EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */
X DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
X p += mcnt; /* Do the jump. */
X DEBUG_PRINT2 ("(to 0x%x).\n", p);
X break;
X
X
X /* We need this opcode so we can detect where alternatives end
X in `group_match_null_string_p' et al. */
X case jump_past_alt:
X DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
X goto unconditional_jump;
X
X
X /* Normally, the on_failure_jump pushes a failure point, which
X then gets popped at pop_failure_jump. We will end up at
X pop_failure_jump, also, and with a pattern of, say, `a+', we
X are skipping over the on_failure_jump, so we have to push
X something meaningless for pop_failure_jump to pop. */
X case dummy_failure_jump:
X DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
X /* It doesn't matter what we push for the string here. What
X the code at `fail' tests is the value for the pattern. */
X PUSH_FAILURE_POINT (0, 0, -2);
X goto unconditional_jump;
X
X
X /* At the end of an alternative, we need to push a dummy failure
X point in case we are followed by a `pop_failure_jump', because
X we don't want the failure point for the alternative to be
X popped. For example, matching `(a|ab)*' against `aab'
X requires that we match the `ab' alternative. */
X case push_dummy_failure:
X DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
X /* See comments just above at `dummy_failure_jump' about the
X two zeroes. */
X PUSH_FAILURE_POINT (0, 0, -2);
X break;
X
X /* Have to succeed matching what follows at least n times.
X After that, handle like `on_failure_jump'. */
X case succeed_n:
X EXTRACT_NUMBER (mcnt, p + 2);
X DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
X
X assert (mcnt >= 0);
X /* Originally, this is how many times we HAVE to succeed. */
X if (mcnt > 0)
X {
X mcnt--;
X p += 2;
X STORE_NUMBER_AND_INCR (p, mcnt);
X DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p, mcnt);
X }
X else if (mcnt == 0)
X {
X DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2);
X p[2] = (unsigned char) no_op;
X p[3] = (unsigned char) no_op;
X goto on_failure;
X }
X break;
X
X case jump_n:
X EXTRACT_NUMBER (mcnt, p + 2);
X DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);
X
X /* Originally, this is how many times we CAN jump. */
X if (mcnt)
X {
X mcnt--;
X STORE_NUMBER (p + 2, mcnt);
X goto unconditional_jump;
X }
X /* If don't have to jump any more, skip over the rest of command. */
X else
X p += 4;
X break;
X
X case set_number_at:
X {
X DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
X
X EXTRACT_NUMBER_AND_INCR (mcnt, p);
X p1 = p + mcnt;
X EXTRACT_NUMBER_AND_INCR (mcnt, p);
X DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt);
X STORE_NUMBER (p1, mcnt);
X break;
X }
X
X case wordbound:
X DEBUG_PRINT1 ("EXECUTING wordbound.\n");
X if (AT_WORD_BOUNDARY (d))
X break;
X goto fail;
X
X case notwordbound:
X DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
X if (AT_WORD_BOUNDARY (d))
X goto fail;
X break;
X
X case wordbeg:
X DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
X if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
X break;
X goto fail;
X
X case wordend:
X DEBUG_PRINT1 ("EXECUTING wordend.\n");
X if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
X && (!WORDCHAR_P (d) || AT_STRINGS_END (d)))
X break;
X goto fail;
X
X#ifdef emacs
X#ifdef emacs19
X case before_dot:
X DEBUG_PRINT1 ("EXECUTING before_dot.\n");
X if (PTR_CHAR_POS ((unsigned char *) d) >= point)
X goto fail;
X break;
X
X case at_dot:
X DEBUG_PRINT1 ("EXECUTING at_dot.\n");
X if (PTR_CHAR_POS ((unsigned char *) d) != point)
X goto fail;
X break;
X
X case after_dot:
X DEBUG_PRINT1 ("EXECUTING after_dot.\n");
X if (PTR_CHAR_POS ((unsigned char *) d) <= point)
X goto fail;
X break;
X#else /* not emacs19 */
X case at_dot:
X DEBUG_PRINT1 ("EXECUTING at_dot.\n");
X if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point)
X goto fail;
X break;
X#endif /* not emacs19 */
X
X case syntaxspec:
X DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
X mcnt = *p++;
X goto matchsyntax;
X
X case wordchar:
X DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
X mcnt = (int) Sword;
X matchsyntax:
X PREFETCH ();
X if (SYNTAX (*d++) != (enum syntaxcode) mcnt)
X goto fail;
X SET_REGS_MATCHED ();
X break;
X
X case notsyntaxspec:
X DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
X mcnt = *p++;
X goto matchnotsyntax;
X
X case notwordchar:
X DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
X mcnt = (int) Sword;
X matchnotsyntax:
X PREFETCH ();
X if (SYNTAX (*d++) == (enum syntaxcode) mcnt)
X goto fail;
X SET_REGS_MATCHED ();
X break;
X
X#else /* not emacs */
X case wordchar:
X DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
X PREFETCH ();
X if (!WORDCHAR_P (d))
X goto fail;
X SET_REGS_MATCHED ();
X d++;
X break;
X
X case notwordchar:
X DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
X PREFETCH ();
X if (WORDCHAR_P (d))
X goto fail;
X SET_REGS_MATCHED ();
X d++;
X break;
X#endif /* not emacs */
X
X default:
X abort ();
X }
X continue; /* Successfully executed one pattern command; keep going. */
X
X
X /* We goto here if a matching operation fails. */
X fail:
X if (!FAIL_STACK_EMPTY ())
X { /* A restart point is known. Restore to that state. */
X DEBUG_PRINT1 ("\nFAIL:\n");
X POP_FAILURE_POINT (d, p,
X lowest_active_reg, highest_active_reg,
X regstart, regend, reg_info);
X
X /* If this failure point is a dummy, try the next one. */
X if (!p)
X goto fail;
X
X /* If we failed to the end of the pattern, don't examine *p. */
X assert (p <= pend);
X if (p < pend)
X {
X boolean is_a_jump_n = false;
X
X /* If failed to a backwards jump that's part of a repetition
X loop, need to pop this failure point and use the next one. */
X switch ((re_opcode_t) *p)
X {
X case jump_n:
X is_a_jump_n = true;
X case maybe_pop_jump:
X case pop_failure_jump:
X case jump:
X p1 = p + 1;
X EXTRACT_NUMBER_AND_INCR (mcnt, p1);
X p1 += mcnt;
X
X if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
X || (!is_a_jump_n
X && (re_opcode_t) *p1 == on_failure_jump))
X goto fail;
X break;
X default:
X /* do nothing */ ;
X }
X }
X
X if (d >= string1 && d <= end1)
X dend = end_match_1;
X }
X else
X break; /* Matching at this starting point really fails. */
X } /* for (;;) */
X
X if (best_regs_set)
X goto restore_best_regs;
X
X FREE_VARIABLES ();
X
X return -1; /* Failure to match. */
X} /* re_match_2 */
X
X/* Subroutine definitions for re_match_2. */
X
X
X/* We are passed P pointing to a register number after a start_memory.
X
X Return true if the pattern up to the corresponding stop_memory can
X match the empty string, and false otherwise.
X
X If we find the matching stop_memory, sets P to point to one past its number.
X Otherwise, sets P to an undefined byte less than or equal to END.
X
X We don't handle duplicates properly (yet). */
X
Xstatic boolean
Xgroup_match_null_string_p (p, end, reg_info)
X unsigned char **p, *end;
X register_info_type *reg_info;
X{
X int mcnt;
X /* Point to after the args to the start_memory. */
X unsigned char *p1 = *p + 2;
X
X while (p1 < end)
X {
X /* Skip over opcodes that can match nothing, and return true or
X false, as appropriate, when we get to one that can't, or to the
X matching stop_memory. */
X
X switch ((re_opcode_t) *p1)
X {
X /* Could be either a loop or a series of alternatives. */
X case on_failure_jump:
X p1++;
X EXTRACT_NUMBER_AND_INCR (mcnt, p1);
X
X /* If the next operation is not a jump backwards in the
X pattern. */
X
X if (mcnt >= 0)
X {
X /* Go through the on_failure_jumps of the alternatives,
X seeing if any of the alternatives cannot match nothing.
X The last alternative starts with only a jump,
X whereas the rest start with on_failure_jump and end
X with a jump, e.g., here is the pattern for `a|b|c':
X
X /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
X /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
X /exactn/1/c
X
X So, we have to first go through the first (n-1)
X alternatives and then deal with the last one separately. */
X
X
X /* Deal with the first (n-1) alternatives, which start
X with an on_failure_jump (see above) that jumps to right
X past a jump_past_alt. */
X
X while ((re_opcode_t) p1[mcnt-3] == jump_past_alt)
X {
X /* `mcnt' holds how many bytes long the alternative
X is, including the ending `jump_past_alt' and
X its number. */
X
X if (!alt_match_null_string_p (p1, p1 + mcnt - 3,
X reg_info))
X return false;
X
X /* Move to right after this alternative, including the
X jump_past_alt. */
X p1 += mcnt;
X
X /* Break if it's the beginning of an n-th alternative
X that doesn't begin with an on_failure_jump. */
X if ((re_opcode_t) *p1 != on_failure_jump)
X break;
X
X /* Still have to check that it's not an n-th
X alternative that starts with an on_failure_jump. */
X p1++;
X EXTRACT_NUMBER_AND_INCR (mcnt, p1);
X if ((re_opcode_t) p1[mcnt-3] != jump_past_alt)
X {
X /* Get to the beginning of the n-th alternative. */
X p1 -= 3;
X break;
X }
X }
X
X /* Deal with the last alternative: go back and get number
X of the `jump_past_alt' just before it. `mcnt' contains
X the length of the alternative. */
X EXTRACT_NUMBER (mcnt, p1 - 2);
X
X if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
X return false;
X
X p1 += mcnt; /* Get past the n-th alternative. */
X } /* if mcnt > 0 */
X break;
X
X
X case stop_memory:
X assert (p1[1] == **p);
X *p = p1 + 2;
X return true;
X
X
X default:
X if (!common_op_match_null_string_p (&p1, end, reg_info))
X return false;
X }
X } /* while p1 < end */
X
X return false;
X} /* group_match_null_string_p */
X
X
X/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
X It expects P to be the first byte of a single alternative and END one
X byte past the last. The alternative can contain groups. */
X
Xstatic boolean
Xalt_match_null_string_p (p, end, reg_info)
X unsigned char *p, *end;
X register_info_type *reg_info;
X{
X int mcnt;
X unsigned char *p1 = p;
X
X while (p1 < end)
X {
X /* Skip over opcodes that can match nothing, and break when we get
X to one that can't. */
X
X switch ((re_opcode_t) *p1)
X {
X /* It's a loop. */
X case on_failure_jump:
X p1++;
X EXTRACT_NUMBER_AND_INCR (mcnt, p1);
X p1 += mcnt;
X break;
X
X default:
X if (!common_op_match_null_string_p (&p1, end, reg_info))
X return false;
X }
X } /* while p1 < end */
X
X return true;
X} /* alt_match_null_string_p */
X
X
X/* Deals with the ops common to group_match_null_string_p and
X alt_match_null_string_p.
X
X Sets P to one after the op and its arguments, if any. */
X
Xstatic boolean
Xcommon_op_match_null_string_p (p, end, reg_info)
X unsigned char **p, *end;
X register_info_type *reg_info;
X{
X int mcnt;
X boolean ret;
X int reg_no;
X unsigned char *p1 = *p;
X
X switch ((re_opcode_t) *p1++)
X {
X case no_op:
X case begline:
X case endline:
X case begbuf:
X case endbuf:
X case wordbeg:
X case wordend:
X case wordbound:
X case notwordbound:
X#ifdef emacs
X case before_dot:
X case at_dot:
X case after_dot:
X#endif
X break;
X
X case start_memory:
X reg_no = *p1;
X assert (reg_no > 0 && reg_no <= MAX_REGNUM);
X ret = group_match_null_string_p (&p1, end, reg_info);
X
X /* Have to set this here in case we're checking a group which
X contains a group and a back reference to it. */
X
X if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
X REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
X
X if (!ret)
X return false;
X break;
X
X /* If this is an optimized succeed_n for zero times, make the jump. */
X case jump:
X EXTRACT_NUMBER_AND_INCR (mcnt, p1);
X if (mcnt >= 0)
X p1 += mcnt;
X else
X return false;
X break;
X
X case succeed_n:
X /* Get to the number of times to succeed. */
X p1 += 2;
X EXTRACT_NUMBER_AND_INCR (mcnt, p1);
X
X if (mcnt == 0)
X {
X p1 -= 4;
X EXTRACT_NUMBER_AND_INCR (mcnt, p1);
X p1 += mcnt;
X }
X else
X return false;
X break;
X
X case duplicate:
X if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
X return false;
X break;
X
X case set_number_at:
X p1 += 4;
X
X default:
X /* All other opcodes mean we cannot match the empty string. */
X return false;
X }
X
X *p = p1;
X return true;
X} /* common_op_match_null_string_p */
X
X
X/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
X bytes; nonzero otherwise. */
X
Xstatic int
Xbcmp_translate (s1, s2, len, translate)
X unsigned char *s1, *s2;
X register int len;
X char *translate;
X{
X register unsigned char *p1 = s1, *p2 = s2;
X while (len)
X {
X if (translate[*p1++] != translate[*p2++]) return 1;
X len--;
X }
X return 0;
X}
X
X/* Entry points for GNU code. */
X
X/* re_compile_pattern is the GNU regular expression compiler: it
X compiles PATTERN (of length SIZE) and puts the result in BUFP.
X Returns 0 if the pattern was valid, otherwise an error string.
X
X Assumes the `allocated' (and perhaps `buffer') and `translate' fields
X are set in BUFP on entry.
X
X We call regex_compile to do the actual compilation. */
X
Xconst char *
Xre_compile_pattern (pattern, length, bufp)
X const char *pattern;
X int length;
X struct re_pattern_buffer *bufp;
X{
X reg_errcode_t ret;
X
X /* GNU code is written to assume at least RE_NREGS registers will be set
X (and at least one extra will be -1). */
X bufp->regs_allocated = REGS_UNALLOCATED;
X
X /* And GNU code determines whether or not to get register information
X by passing null for the REGS argument to re_match, etc., not by
X setting no_sub. */
X bufp->no_sub = 0;
X
X /* Match anchors at newline. */
X bufp->newline_anchor = 1;
X
X ret = regex_compile (pattern, length, re_syntax_options, bufp);
X
X return re_error_msg[(int) ret];
X}
X
X/* Entry points compatible with 4.2 BSD regex library. We don't define
X them if this is an Emacs or POSIX compilation. */
X
X#if !defined (emacs) && !defined (_POSIX_SOURCE)
X
X/* BSD has one and only one pattern buffer. */
Xstatic struct re_pattern_buffer re_comp_buf;
X
Xchar *
Xre_comp (s)
X const char *s;
X{
X reg_errcode_t ret;
X
X if (!s)
X {
X if (!re_comp_buf.buffer)
X return "No previous regular expression";
X return 0;
X }
X
X if (!re_comp_buf.buffer)
X {
X re_comp_buf.buffer = (unsigned char *) malloc (200);
X if (re_comp_buf.buffer == NULL)
X return "Memory exhausted";
X re_comp_buf.allocated = 200;
X
X re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
X if (re_comp_buf.fastmap == NULL)
X return "Memory exhausted";
X }
X
X /* Since `re_exec' always passes NULL for the `regs' argument, we
X don't need to initialize the pattern buffer fields which affect it. */
X
X /* Match anchors at newlines. */
X re_comp_buf.newline_anchor = 1;
X
X ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
X
X /* Yes, we're discarding `const' here. */
X return (char *) re_error_msg[(int) ret];
X}
X
X
Xint
Xre_exec (s)
X const char *s;
X{
X const int len = strlen (s);
X return
X 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
X}
X#endif /* not emacs and not _POSIX_SOURCE */
X
X/* POSIX.2 functions. Don't define these for Emacs. */
X
X#ifndef emacs
X
X/* regcomp takes a regular expression as a string and compiles it.
X
X PREG is a regex_t *. We do not expect any fields to be initialized,
X since POSIX says we shouldn't. Thus, we set
X
X `buffer' to the compiled pattern;
X `used' to the length of the compiled pattern;
X `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
X REG_EXTENDED bit in CFLAGS is set; otherwise, to
X RE_SYNTAX_POSIX_BASIC;
X `newline_anchor' to REG_NEWLINE being set in CFLAGS;
X `fastmap' and `fastmap_accurate' to zero;
X `re_nsub' to the number of subexpressions in PATTERN.
X
X PATTERN is the address of the pattern string.
X
X CFLAGS is a series of bits which affect compilation.
X
X If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
X use POSIX basic syntax.
X
X If REG_NEWLINE is set, then . and [^...] don't match newline.
X Also, regexec will try a match beginning after every newline.
X
X If REG_ICASE is set, then we considers upper- and lowercase
X versions of letters to be equivalent when matching.
X
X If REG_NOSUB is set, then when PREG is passed to regexec, that
X routine will report only success or failure, and nothing about the
X registers.
X
X It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
X the return codes and their meanings.) */
X
Xint
Xregcomp (preg, pattern, cflags)
X regex_t *preg;
X const char *pattern;
X int cflags;
X{
X reg_errcode_t ret;
X unsigned syntax
X = (cflags & REG_EXTENDED) ?
X RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
X
X /* regex_compile will allocate the space for the compiled pattern. */
X preg->buffer = 0;
X preg->allocated = 0;
X
X /* Don't bother to use a fastmap when searching. This simplifies the
X REG_NEWLINE case: if we used a fastmap, we'd have to put all the
X characters after newlines into the fastmap. This way, we just try
X every character. */
X preg->fastmap = 0;
X
X if (cflags & REG_ICASE)
X {
X unsigned i;
X
X preg->translate = (char *) malloc (CHAR_SET_SIZE);
X if (preg->translate == NULL)
X return (int) REG_ESPACE;
X
X /* Map uppercase characters to corresponding lowercase ones. */
X for (i = 0; i < CHAR_SET_SIZE; i++)
X preg->translate[i] = ISUPPER (i) ? tolower (i) : i;
X }
X else
X preg->translate = NULL;
X
X /* If REG_NEWLINE is set, newlines are treated differently. */
X if (cflags & REG_NEWLINE)
X { /* REG_NEWLINE implies neither . nor [^...] match newline. */
X syntax &= ~RE_DOT_NEWLINE;
X syntax |= RE_HAT_LISTS_NOT_NEWLINE;
X /* It also changes the matching behavior. */
X preg->newline_anchor = 1;
X }
X else
X preg->newline_anchor = 0;
X
X preg->no_sub = !!(cflags & REG_NOSUB);
X
X /* POSIX says a null character in the pattern terminates it, so we
X can use strlen here in compiling the pattern. */
X ret = regex_compile (pattern, strlen (pattern), syntax, preg);
X
X /* POSIX doesn't distinguish between an unmatched open-group and an
X unmatched close-group: both are REG_EPAREN. */
X if (ret == REG_ERPAREN) ret = REG_EPAREN;
X
X return (int) ret;
X}
X
X
X/* regexec searches for a given pattern, specified by PREG, in the
X string STRING.
X
X If NMATCH is zero or REG_NOSUB was set in the cflags argument to
X `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
X least NMATCH elements, and we set them to the offsets of the
X corresponding matched substrings.
X
X EFLAGS specifies `execution flags' which affect matching: if
X REG_NOTBOL is set, then ^ does not match at the beginning of the
X string; if REG_NOTEOL is set, then $ does not match at the end.
X
X We return 0 if we find a match and REG_NOMATCH if not. */
X
Xint
Xregexec (preg, string, nmatch, pmatch, eflags)
X const regex_t *preg;
X const char *string;
X size_t nmatch;
X regmatch_t pmatch[];
X int eflags;
X{
X int ret;
X struct re_registers regs;
X regex_t private_preg;
X int len = strlen (string);
X boolean want_reg_info = !preg->no_sub && nmatch > 0;
X
X private_preg = *preg;
X
X private_preg.not_bol = !!(eflags & REG_NOTBOL);
X private_preg.not_eol = !!(eflags & REG_NOTEOL);
X
X /* The user has told us exactly how many registers to return
X information about, via `nmatch'. We have to pass that on to the
X matching routines. */
X private_preg.regs_allocated = REGS_FIXED;
X
X if (want_reg_info)
X {
X regs.num_regs = nmatch;
X regs.start = TALLOC (nmatch, regoff_t);
X regs.end = TALLOC (nmatch, regoff_t);
X if (regs.start == NULL || regs.end == NULL)
X return (int) REG_NOMATCH;
X }
X
X /* Perform the searching operation. */
X ret = re_search (&private_preg, string, len,
X /* start: */ 0, /* range: */ len,
X want_reg_info ? ®s : (struct re_registers *) 0);
X
X /* Copy the register information to the POSIX structure. */
X if (want_reg_info)
X {
X if (ret >= 0)
X {
X unsigned r;
X
X for (r = 0; r < nmatch; r++)
X {
X pmatch[r].rm_so = regs.start[r];
X pmatch[r].rm_eo = regs.end[r];
X }
X }
X
X /* If we needed the temporary register info, free the space now. */
X free (regs.start);
X free (regs.end);
X }
X
X /* We want zero return to mean success, unlike `re_search'. */
X return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
X}
X
X
X/* Returns a message corresponding to an error code, ERRCODE, returned
X from either regcomp or regexec. We don't use PREG here. */
X
Xsize_t
Xregerror (errcode, preg, errbuf, errbuf_size)
X int errcode;
X const regex_t *preg;
X char *errbuf;
X size_t errbuf_size;
X{
X const char *msg;
X size_t msg_size;
X
X if (errcode < 0
X || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0])))
X /* Only error codes returned by the rest of the code should be passed
X to this routine. If we are given anything else, or if other regex
X code generates an invalid error code, then the program has a bug.
X Dump core so we can fix it. */
X abort ();
X
X msg_size = strlen (msg) + 1; /* Includes the null. */
X
X if (errbuf_size != 0)
X {
X if (msg_size > errbuf_size)
X {
X strncpy (errbuf, msg, errbuf_size - 1);
X errbuf[errbuf_size - 1] = 0;
X }
X else
X strcpy (errbuf, msg);
X }
X
X return msg_size;
X}
X
X
X/* Free dynamically allocated space used by PREG. */
X
Xvoid
Xregfree (preg)
X regex_t *preg;
X{
X if (preg->buffer != NULL)
X free (preg->buffer);
X preg->buffer = NULL;
X
X preg->allocated = 0;
X preg->used = 0;
X
X if (preg->fastmap != NULL)
X free (preg->fastmap);
X preg->fastmap = NULL;
X preg->fastmap_accurate = 0;
X
X if (preg->translate != NULL)
X free (preg->translate);
X preg->translate = NULL;
X}
X
X#endif /* not emacs */
X
X/*
XLocal variables:
Xmake-backup-files: t
Xversion-control: t
Xtrim-versions-without-asking: nil
XEnd:
X*/
END_OF_FILE
if test 161195 -ne `wc -c <'regex.c'`; then
echo shar: \"'regex.c'\" unpacked with wrong size!
fi
# end of 'regex.c'
fi
if test -f 'getdate.y' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'getdate.y'\"
else
echo shar: Extracting \"'getdate.y'\" \(22885 characters\)
sed "s/^X//" >'getdate.y' <<'END_OF_FILE'
X%{
X/* $Revision: 2.1 $
X**
X** Originally written by Steven M. Bellovin <smb@research.att.com> while
X** at the University of North Carolina at Chapel Hill. Later tweaked by
X** a couple of people on Usenet. Completely overhauled by Rich $alz
X** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
X** send any email to Rich.
X**
X** This grammar has eight shift/reduce conflicts.
X**
X** This code is in the public domain and has no copyright.
X*/
X/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */
X/* SUPPRESS 288 on yyerrlab *//* Label unused */
X
X#ifdef HAVE_CONFIG_H
X#include "config.h"
X#endif
X
X#ifdef __GNUC__
X#define alloca __builtin_alloca
X#else
X#ifdef HAVE_ALLOCA_H
X#include <alloca.h>
X#else
X#ifdef _AIX /* for Bison */
X #pragma alloca
X#else
Xchar *alloca ();
X#endif
X#endif
X#endif
X
X#include <stdio.h>
X#include <ctype.h>
X
X/* The code at the top of get_date which figures out the offset of the
X current time zone checks various CPP symbols to see if special
X tricks are need, but defaults to using the gettimeofday system call.
X Include <sys/time.h> if that will be used. */
X
X#if !defined (USG) && !defined (sgi) && !defined (__386BSD__)
X#include <sys/time.h>
X#endif
X
X#if defined(vms)
X
X#include <types.h>
X#include <time.h>
X
X#else
X
X#include <sys/types.h>
X
X#if defined(USG) || !defined(HAVE_FTIME)
X/*
X** If you need to do a tzset() call to set the
X** timezone, and don't have ftime().
X*/
Xstruct timeb {
X time_t time; /* Seconds since the epoch */
X unsigned short millitm; /* Field not used */
X short timezone;
X short dstflag; /* Field not used */
X};
X
X#else
X
X#include <sys/timeb.h>
X
X#endif /* defined(USG) && !defined(HAVE_FTIME) */
X
X#if defined(BSD4_2) || defined(BSD4_1C) || (defined (hp9000) && !defined (hpux))
X#include <sys/time.h>
X#else
X#if defined(_AIX)
X#include <sys/time.h>
X#endif
X#include <time.h>
X#endif /* defined(BSD4_2) */
X
X#endif /* defined(vms) */
X
X#if defined (STDC_HEADERS) || defined (USG)
X#include <string.h>
X#endif
X
X#if sgi
X#undef timezone
X#endif
X
Xextern struct tm *localtime();
X
X#define yyparse getdate_yyparse
X#define yylex getdate_yylex
X#define yyerror getdate_yyerror
X
X#if !defined(lint) && !defined(SABER)
Xstatic char RCS[] =
X "$Header: str2date.y,v 2.1 90/09/06 08:15:06 cronan Exp $";
X#endif /* !defined(lint) && !defined(SABER) */
X
X
X#define EPOCH 1970
X#define HOUR(x) ((time_t)(x) * 60)
X#define SECSPERDAY (24L * 60L * 60L)
X
X
X/*
X** An entry in the lexical lookup table.
X*/
Xtypedef struct _TABLE {
X char *name;
X int type;
X time_t value;
X} TABLE;
X
X
X/*
X** Daylight-savings mode: on, off, or not yet known.
X*/
Xtypedef enum _DSTMODE {
X DSTon, DSToff, DSTmaybe
X} DSTMODE;
X
X/*
X** Meridian: am, pm, or 24-hour style.
X*/
Xtypedef enum _MERIDIAN {
X MERam, MERpm, MER24
X} MERIDIAN;
X
X
X/*
X** Global variables. We could get rid of most of these by using a good
X** union as the yacc stack. (This routine was originally written before
X** yacc had the %union construct.) Maybe someday; right now we only use
X** the %union very rarely.
X*/
Xstatic char *yyInput;
Xstatic DSTMODE yyDSTmode;
Xstatic time_t yyDayOrdinal;
Xstatic time_t yyDayNumber;
Xstatic int yyHaveDate;
Xstatic int yyHaveDay;
Xstatic int yyHaveRel;
Xstatic int yyHaveTime;
Xstatic int yyHaveZone;
Xstatic time_t yyTimezone;
Xstatic time_t yyDay;
Xstatic time_t yyHour;
Xstatic time_t yyMinutes;
Xstatic time_t yyMonth;
Xstatic time_t yySeconds;
Xstatic time_t yyYear;
Xstatic MERIDIAN yyMeridian;
Xstatic time_t yyRelMonth;
Xstatic time_t yyRelSeconds;
X
X%}
X
X%union {
X time_t Number;
X enum _MERIDIAN Meridian;
X}
X
X%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
X%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
X
X%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
X%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
X%type <Meridian> tMERIDIAN o_merid
X
X%%
X
Xspec : /* NULL */
X | spec item
X ;
X
Xitem : time {
X yyHaveTime++;
X }
X | zone {
X yyHaveZone++;
X }
X | date {
X yyHaveDate++;
X }
X | day {
X yyHaveDay++;
X }
X | rel {
X yyHaveRel++;
X }
X | number
X ;
X
Xtime : tUNUMBER tMERIDIAN {
X yyHour = $1;
X yyMinutes = 0;
X yySeconds = 0;
X yyMeridian = $2;
X }
X | tUNUMBER ':' tUNUMBER o_merid {
X yyHour = $1;
X yyMinutes = $3;
X yySeconds = 0;
X yyMeridian = $4;
X }
X | tUNUMBER ':' tUNUMBER tSNUMBER {
X yyHour = $1;
X yyMinutes = $3;
X yyMeridian = MER24;
X yyDSTmode = DSToff;
X yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
X }
X | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
X yyHour = $1;
X yyMinutes = $3;
X yySeconds = $5;
X yyMeridian = $6;
X }
X | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
X yyHour = $1;
X yyMinutes = $3;
X yySeconds = $5;
X yyMeridian = MER24;
X yyDSTmode = DSToff;
X yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
X }
X ;
X
Xzone : tZONE {
X yyTimezone = $1;
X yyDSTmode = DSToff;
X }
X | tDAYZONE {
X yyTimezone = $1;
X yyDSTmode = DSTon;
X }
X |
X tZONE tDST {
X yyTimezone = $1;
X yyDSTmode = DSTon;
X }
X ;
X
Xday : tDAY {
X yyDayOrdinal = 1;
X yyDayNumber = $1;
X }
X | tDAY ',' {
X yyDayOrdinal = 1;
X yyDayNumber = $1;
X }
X | tUNUMBER tDAY {
X yyDayOrdinal = $1;
X yyDayNumber = $2;
X }
X ;
X
Xdate : tUNUMBER '/' tUNUMBER {
X yyMonth = $1;
X yyDay = $3;
X }
X | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
X yyMonth = $1;
X yyDay = $3;
X yyYear = $5;
X }
X | tUNUMBER tSNUMBER tSNUMBER {
X /* ISO 8601 format. yyyy-mm-dd. */
X yyYear = $1;
X yyMonth = -$2;
X yyDay = -$3;
X }
X | tMONTH tUNUMBER {
X yyMonth = $1;
X yyDay = $2;
X }
X | tMONTH tUNUMBER ',' tUNUMBER {
X yyMonth = $1;
X yyDay = $2;
X yyYear = $4;
X }
X | tUNUMBER tMONTH {
X yyMonth = $2;
X yyDay = $1;
X }
X | tUNUMBER tMONTH tUNUMBER {
X yyMonth = $2;
X yyDay = $1;
X yyYear = $3;
X }
X ;
X
Xrel : relunit tAGO {
X yyRelSeconds = -yyRelSeconds;
X yyRelMonth = -yyRelMonth;
X }
X | relunit
X ;
X
Xrelunit : tUNUMBER tMINUTE_UNIT {
X yyRelSeconds += $1 * $2 * 60L;
X }
X | tSNUMBER tMINUTE_UNIT {
X yyRelSeconds += $1 * $2 * 60L;
X }
X | tMINUTE_UNIT {
X yyRelSeconds += $1 * 60L;
X }
X | tSNUMBER tSEC_UNIT {
X yyRelSeconds += $1;
X }
X | tUNUMBER tSEC_UNIT {
X yyRelSeconds += $1;
X }
X | tSEC_UNIT {
X yyRelSeconds++;
X }
X | tSNUMBER tMONTH_UNIT {
X yyRelMonth += $1 * $2;
X }
X | tUNUMBER tMONTH_UNIT {
X yyRelMonth += $1 * $2;
X }
X | tMONTH_UNIT {
X yyRelMonth += $1;
X }
X ;
X
Xnumber : tUNUMBER {
X if (yyHaveTime && yyHaveDate && !yyHaveRel)
X yyYear = $1;
X else {
X if($1>10000) {
X time_t date_part;
X
X date_part= $1/10000;
X yyHaveDate++;
X yyDay= (date_part)%100;
X yyMonth= (date_part/100)%100;
X yyYear = date_part/10000;
X }
X yyHaveTime++;
X if ($1 < 100) {
X yyHour = $1;
X yyMinutes = 0;
X }
X else {
X yyHour = $1 / 100;
X yyMinutes = $1 % 100;
X }
X yySeconds = 0;
X yyMeridian = MER24;
X }
X }
X ;
X
Xo_merid : /* NULL */ {
X $$ = MER24;
X }
X | tMERIDIAN {
X $$ = $1;
X }
X ;
X
X%%
X
X/* Month and day table. */
Xstatic TABLE const MonthDayTable[] = {
X { "january", tMONTH, 1 },
X { "february", tMONTH, 2 },
X { "march", tMONTH, 3 },
X { "april", tMONTH, 4 },
X { "may", tMONTH, 5 },
X { "june", tMONTH, 6 },
X { "july", tMONTH, 7 },
X { "august", tMONTH, 8 },
X { "september", tMONTH, 9 },
X { "sept", tMONTH, 9 },
X { "october", tMONTH, 10 },
X { "november", tMONTH, 11 },
X { "december", tMONTH, 12 },
X { "sunday", tDAY, 0 },
X { "monday", tDAY, 1 },
X { "tuesday", tDAY, 2 },
X { "tues", tDAY, 2 },
X { "wednesday", tDAY, 3 },
X { "wednes", tDAY, 3 },
X { "thursday", tDAY, 4 },
X { "thur", tDAY, 4 },
X { "thurs", tDAY, 4 },
X { "friday", tDAY, 5 },
X { "saturday", tDAY, 6 },
X { NULL }
X};
X
X/* Time units table. */
Xstatic TABLE const UnitsTable[] = {
X { "year", tMONTH_UNIT, 12 },
X { "month", tMONTH_UNIT, 1 },
X { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
X { "week", tMINUTE_UNIT, 7 * 24 * 60 },
X { "day", tMINUTE_UNIT, 1 * 24 * 60 },
X { "hour", tMINUTE_UNIT, 60 },
X { "minute", tMINUTE_UNIT, 1 },
X { "min", tMINUTE_UNIT, 1 },
X { "second", tSEC_UNIT, 1 },
X { "sec", tSEC_UNIT, 1 },
X { NULL }
X};
X
X/* Assorted relative-time words. */
Xstatic TABLE const OtherTable[] = {
X { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
X { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
X { "today", tMINUTE_UNIT, 0 },
X { "now", tMINUTE_UNIT, 0 },
X { "last", tUNUMBER, -1 },
X { "this", tMINUTE_UNIT, 0 },
X { "next", tUNUMBER, 2 },
X { "first", tUNUMBER, 1 },
X/* { "second", tUNUMBER, 2 }, */
X { "third", tUNUMBER, 3 },
X { "fourth", tUNUMBER, 4 },
X { "fifth", tUNUMBER, 5 },
X { "sixth", tUNUMBER, 6 },
X { "seventh", tUNUMBER, 7 },
X { "eighth", tUNUMBER, 8 },
X { "ninth", tUNUMBER, 9 },
X { "tenth", tUNUMBER, 10 },
X { "eleventh", tUNUMBER, 11 },
X { "twelfth", tUNUMBER, 12 },
X { "ago", tAGO, 1 },
X { NULL }
X};
X
X/* The timezone table. */
X/* Some of these are commented out because a time_t can't store a float. */
Xstatic TABLE const TimezoneTable[] = {
X { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
X { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
X { "utc", tZONE, HOUR( 0) },
X { "wet", tZONE, HOUR( 0) }, /* Western European */
X { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
X { "wat", tZONE, HOUR( 1) }, /* West Africa */
X { "at", tZONE, HOUR( 2) }, /* Azores */
X#if 0
X /* For completeness. BST is also British Summer, and GST is
X * also Guam Standard. */
X { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
X { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
X#endif
X#if 0
X { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
X { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
X { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
X#endif
X { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
X { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
X { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
X { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
X { "cst", tZONE, HOUR( 6) }, /* Central Standard */
X { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
X { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
X { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
X { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
X { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
X { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
X { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
X { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
X { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
X { "cat", tZONE, HOUR(10) }, /* Central Alaska */
X { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
X { "nt", tZONE, HOUR(11) }, /* Nome */
X { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
X { "cet", tZONE, -HOUR(1) }, /* Central European */
X { "met", tZONE, -HOUR(1) }, /* Middle European */
X { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
X { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
X { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
X { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
X { "fwt", tZONE, -HOUR(1) }, /* French Winter */
X { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
X { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
X { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
X#if 0
X { "it", tZONE, -HOUR(3.5) },/* Iran */
X#endif
X { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
X { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
X#if 0
X { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
X#endif
X { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
X#if 0
X /* For completeness. NST is also Newfoundland Stanard, and SST is
X * also Swedish Summer. */
X { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
X { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
X#endif /* 0 */
X { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
X { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
X#if 0
X { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
X#endif
X { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
X { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
X#if 0
X { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
X { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
X#endif
X { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
X { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
X { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
X { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
X { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
X { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
X { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
X { NULL }
X};
X
X/* Military timezone table. */
Xstatic TABLE const MilitaryTable[] = {
X { "a", tZONE, HOUR( 1) },
X { "b", tZONE, HOUR( 2) },
X { "c", tZONE, HOUR( 3) },
X { "d", tZONE, HOUR( 4) },
X { "e", tZONE, HOUR( 5) },
X { "f", tZONE, HOUR( 6) },
X { "g", tZONE, HOUR( 7) },
X { "h", tZONE, HOUR( 8) },
X { "i", tZONE, HOUR( 9) },
X { "k", tZONE, HOUR( 10) },
X { "l", tZONE, HOUR( 11) },
X { "m", tZONE, HOUR( 12) },
X { "n", tZONE, HOUR(- 1) },
X { "o", tZONE, HOUR(- 2) },
X { "p", tZONE, HOUR(- 3) },
X { "q", tZONE, HOUR(- 4) },
X { "r", tZONE, HOUR(- 5) },
X { "s", tZONE, HOUR(- 6) },
X { "t", tZONE, HOUR(- 7) },
X { "u", tZONE, HOUR(- 8) },
X { "v", tZONE, HOUR(- 9) },
X { "w", tZONE, HOUR(-10) },
X { "x", tZONE, HOUR(-11) },
X { "y", tZONE, HOUR(-12) },
X { "z", tZONE, HOUR( 0) },
X { NULL }
X};
X
X
X
X
X/* ARGSUSED */
Xstatic int
Xyyerror(s)
X char *s;
X{
X return 0;
X}
X
X
Xstatic time_t
XToSeconds(Hours, Minutes, Seconds, Meridian)
X time_t Hours;
X time_t Minutes;
X time_t Seconds;
X MERIDIAN Meridian;
X{
X if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
X return -1;
X switch (Meridian) {
X case MER24:
X if (Hours < 0 || Hours > 23)
X return -1;
X return (Hours * 60L + Minutes) * 60L + Seconds;
X case MERam:
X if (Hours < 1 || Hours > 12)
X return -1;
X return (Hours * 60L + Minutes) * 60L + Seconds;
X case MERpm:
X if (Hours < 1 || Hours > 12)
X return -1;
X return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
X }
X /* NOTREACHED */
X}
X
X
Xstatic time_t
XConvert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
X time_t Month;
X time_t Day;
X time_t Year;
X time_t Hours;
X time_t Minutes;
X time_t Seconds;
X MERIDIAN Meridian;
X DSTMODE DSTmode;
X{
X static int DaysInMonth[12] = {
X 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X };
X time_t tod;
X time_t Julian;
X int i;
X
X if (Year < 0)
X Year = -Year;
X if (Year < 100)
X Year += 1900;
X DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
X ? 29 : 28;
X if (Year < EPOCH || Year > 1999
X || Month < 1 || Month > 12
X /* Lint fluff: "conversion from long may lose accuracy" */
X || Day < 1 || Day > DaysInMonth[(int)--Month])
X return -1;
X
X for (Julian = Day - 1, i = 0; i < Month; i++)
X Julian += DaysInMonth[i];
X for (i = EPOCH; i < Year; i++)
X Julian += 365 + (i % 4 == 0);
X Julian *= SECSPERDAY;
X Julian += yyTimezone * 60L;
X if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
X return -1;
X Julian += tod;
X if (DSTmode == DSTon
X || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
X Julian -= 60 * 60;
X return Julian;
X}
X
X
Xstatic time_t
XDSTcorrect(Start, Future)
X time_t Start;
X time_t Future;
X{
X time_t StartDay;
X time_t FutureDay;
X
X StartDay = (localtime(&Start)->tm_hour + 1) % 24;
X FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
X return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
X}
X
X
Xstatic time_t
XRelativeDate(Start, DayOrdinal, DayNumber)
X time_t Start;
X time_t DayOrdinal;
X time_t DayNumber;
X{
X struct tm *tm;
X time_t now;
X
X now = Start;
X tm = localtime(&now);
X now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
X now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
X return DSTcorrect(Start, now);
X}
X
X
Xstatic time_t
XRelativeMonth(Start, RelMonth)
X time_t Start;
X time_t RelMonth;
X{
X struct tm *tm;
X time_t Month;
X time_t Year;
X
X if (RelMonth == 0)
X return 0;
X tm = localtime(&Start);
X Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
X Year = Month / 12;
X Month = Month % 12 + 1;
X return DSTcorrect(Start,
X Convert(Month, (time_t)tm->tm_mday, Year,
X (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
X MER24, DSTmaybe));
X}
X
X
Xstatic int
XLookupWord(buff)
X char *buff;
X{
X register char *p;
X register char *q;
X register const TABLE *tp;
X int i;
X int abbrev;
X
X /* Make it lowercase. */
X for (p = buff; *p; p++)
X if (isupper(*p))
X *p = tolower(*p);
X
X if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
X yylval.Meridian = MERam;
X return tMERIDIAN;
X }
X if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
X yylval.Meridian = MERpm;
X return tMERIDIAN;
X }
X
X /* See if we have an abbreviation for a month. */
X if (strlen(buff) == 3)
X abbrev = 1;
X else if (strlen(buff) == 4 && buff[3] == '.') {
X abbrev = 1;
X buff[3] = '\0';
X }
X else
X abbrev = 0;
X
X for (tp = MonthDayTable; tp->name; tp++) {
X if (abbrev) {
X if (strncmp(buff, tp->name, 3) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X }
X else if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X }
X
X for (tp = TimezoneTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X
X if (strcmp(buff, "dst") == 0)
X return tDST;
X
X for (tp = UnitsTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X
X /* Strip off any plural and try the units table again. */
X i = strlen(buff) - 1;
X if (buff[i] == 's') {
X buff[i] = '\0';
X for (tp = UnitsTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X buff[i] = 's'; /* Put back for "this" in OtherTable. */
X }
X
X for (tp = OtherTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X
X /* Military timezones. */
X if (buff[1] == '\0' && isalpha(*buff)) {
X for (tp = MilitaryTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X }
X
X /* Drop out any periods and try the timezone table again. */
X for (i = 0, p = q = buff; *q; q++)
X if (*q != '.')
X *p++ = *q;
X else
X i++;
X *p = '\0';
X if (i)
X for (tp = TimezoneTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X
X return tID;
X}
X
X
Xstatic int
Xyylex()
X{
X register char c;
X register char *p;
X char buff[20];
X int Count;
X int sign;
X
X for ( ; ; ) {
X while (isspace(*yyInput))
X yyInput++;
X
X if (isdigit(c = *yyInput) || c == '-' || c == '+') {
X if (c == '-' || c == '+') {
X sign = c == '-' ? -1 : 1;
X if (!isdigit(*++yyInput))
X /* skip the '-' sign */
X continue;
X }
X else
X sign = 0;
X for (yylval.Number = 0; isdigit(c = *yyInput++); )
X yylval.Number = 10 * yylval.Number + c - '0';
X yyInput--;
X if (sign < 0)
X yylval.Number = -yylval.Number;
X return sign ? tSNUMBER : tUNUMBER;
X }
X if (isalpha(c)) {
X for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
X if (p < &buff[sizeof buff - 1])
X *p++ = c;
X *p = '\0';
X yyInput--;
X return LookupWord(buff);
X }
X if (c != '(')
X return *yyInput++;
X Count = 0;
X do {
X c = *yyInput++;
X if (c == '\0')
X return c;
X if (c == '(')
X Count++;
X else if (c == ')')
X Count--;
X } while (Count > 0);
X }
X}
X
X
Xtime_t
Xget_date(p, now)
X char *p;
X struct timeb *now;
X{
X struct tm *tm;
X struct timeb ftz;
X time_t Start;
X time_t tod;
X
X yyInput = p;
X if (now == NULL) {
X now = &ftz;
X#if !defined(HAVE_FTIME)
X (void)time(&ftz.time);
X /* Set the timezone global. */
X tzset();
X {
X#if sgi
X ftz.timezone = (int) _timezone / 60;
X#else /* not sgi */
X#ifdef __386BSD__
X ftz.timezone = 0;
X#else /* neither sgi nor 386BSD */
X#if defined (USG)
X extern time_t timezone;
X
X ftz.timezone = (int) timezone / 60;
X#else /* neither sgi nor 386BSD nor USG */
X struct timeval tv;
X struct timezone tz;
X
X gettimeofday (&tv, &tz);
X ftz.timezone = (int) tz.tz_minuteswest;
X#endif /* neither sgi nor 386BSD nor USG */
X#endif /* neither sgi nor 386BSD */
X#endif /* not sgi */
X }
X#else /* HAVE_FTIME */
X (void)ftime(&ftz);
X#endif /* HAVE_FTIME */
X }
X
X tm = localtime(&now->time);
X yyYear = tm->tm_year;
X yyMonth = tm->tm_mon + 1;
X yyDay = tm->tm_mday;
X yyTimezone = now->timezone;
X yyDSTmode = DSTmaybe;
X yyHour = 0;
X yyMinutes = 0;
X yySeconds = 0;
X yyMeridian = MER24;
X yyRelSeconds = 0;
X yyRelMonth = 0;
X yyHaveDate = 0;
X yyHaveDay = 0;
X yyHaveRel = 0;
X yyHaveTime = 0;
X yyHaveZone = 0;
X
X if (yyparse()
X || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
X return -1;
X
X if (yyHaveDate || yyHaveTime || yyHaveDay) {
X Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
X yyMeridian, yyDSTmode);
X if (Start < 0)
X return -1;
X }
X else {
X Start = now->time;
X if (!yyHaveRel)
X Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
X }
X
X Start += yyRelSeconds;
X Start += RelativeMonth(Start, yyRelMonth);
X
X if (yyHaveDay && !yyHaveDate) {
X tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
X Start += tod;
X }
X
X /* Have to do *something* with a legitimate -1 so it's distinguishable
X * from the error return value. (Alternately could set errno on error.) */
X return Start == -1 ? 0 : Start;
X}
X
X
X#if defined(TEST)
X
X/* ARGSUSED */
Xmain(ac, av)
X int ac;
X char *av[];
X{
X char buff[128];
X time_t d;
X
X (void)printf("Enter date, or blank line to exit.\n\t> ");
X (void)fflush(stdout);
X while (gets(buff) && buff[0]) {
X d = get_date(buff, (struct timeb *)NULL);
X if (d == -1)
X (void)printf("Bad format - couldn't convert.\n");
X else
X (void)printf("%s", ctime(&d));
X (void)printf("\t> ");
X (void)fflush(stdout);
X }
X exit(0);
X /* NOTREACHED */
X}
X#endif /* defined(TEST) */
END_OF_FILE
if test 22885 -ne `wc -c <'getdate.y'`; then
echo shar: \"'getdate.y'\" unpacked with wrong size!
fi
# end of 'getdate.y'
fi
if test -f 'getdate.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'getdate.c'\"
else
echo shar: Extracting \"'getdate.c'\" \(46681 characters\)
sed "s/^X//" >'getdate.c' <<'END_OF_FILE'
X
X/* A Bison parser, made from ./getdate.y */
X
X#define YYBISON 1 /* Identify Bison output. */
X
X#define tAGO 258
X#define tDAY 259
X#define tDAYZONE 260
X#define tID 261
X#define tMERIDIAN 262
X#define tMINUTE_UNIT 263
X#define tMONTH 264
X#define tMONTH_UNIT 265
X#define tSEC_UNIT 266
X#define tSNUMBER 267
X#define tUNUMBER 268
X#define tZONE 269
X#define tDST 270
X
X#line 1 "./getdate.y"
X
X/* $Revision: 2.1 $
X**
X** Originally written by Steven M. Bellovin <smb@research.att.com> while
X** at the University of North Carolina at Chapel Hill. Later tweaked by
X** a couple of people on Usenet. Completely overhauled by Rich $alz
X** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
X** send any email to Rich.
X**
X** This grammar has eight shift/reduce conflicts.
X**
X** This code is in the public domain and has no copyright.
X*/
X/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */
X/* SUPPRESS 288 on yyerrlab *//* Label unused */
X
X#ifdef HAVE_CONFIG_H
X#include "config.h"
X#endif
X
X#ifdef __GNUC__
X#define alloca __builtin_alloca
X#else
X#ifdef HAVE_ALLOCA_H
X#include <alloca.h>
X#else
X#ifdef _AIX /* for Bison */
X #pragma alloca
X#else
Xchar *alloca ();
X#endif
X#endif
X#endif
X
X#include <stdio.h>
X#include <ctype.h>
X
X/* The code at the top of get_date which figures out the offset of the
X current time zone checks various CPP symbols to see if special
X tricks are need, but defaults to using the gettimeofday system call.
X Include <sys/time.h> if that will be used. */
X
X#if !defined (USG) && !defined (sgi) && !defined (__386BSD__)
X#include <sys/time.h>
X#endif
X
X#if defined(vms)
X
X#include <types.h>
X#include <time.h>
X
X#else
X
X#include <sys/types.h>
X
X#if defined(USG) || !defined(HAVE_FTIME)
X/*
X** If you need to do a tzset() call to set the
X** timezone, and don't have ftime().
X*/
Xstruct timeb {
X time_t time; /* Seconds since the epoch */
X unsigned short millitm; /* Field not used */
X short timezone;
X short dstflag; /* Field not used */
X};
X
X#else
X
X#include <sys/timeb.h>
X
X#endif /* defined(USG) && !defined(HAVE_FTIME) */
X
X#if defined(BSD4_2) || defined(BSD4_1C) || (defined (hp9000) && !defined (hpux))
X#include <sys/time.h>
X#else
X#if defined(_AIX)
X#include <sys/time.h>
X#endif
X#include <time.h>
X#endif /* defined(BSD4_2) */
X
X#endif /* defined(vms) */
X
X#if defined (STDC_HEADERS) || defined (USG)
X#include <string.h>
X#endif
X
X#if sgi
X#undef timezone
X#endif
X
Xextern struct tm *localtime();
X
X#define yyparse getdate_yyparse
X#define yylex getdate_yylex
X#define yyerror getdate_yyerror
X
X#if !defined(lint) && !defined(SABER)
Xstatic char RCS[] =
X "$Header: str2date.y,v 2.1 90/09/06 08:15:06 cronan Exp $";
X#endif /* !defined(lint) && !defined(SABER) */
X
X
X#define EPOCH 1970
X#define HOUR(x) ((time_t)(x) * 60)
X#define SECSPERDAY (24L * 60L * 60L)
X
X
X/*
X** An entry in the lexical lookup table.
X*/
Xtypedef struct _TABLE {
X char *name;
X int type;
X time_t value;
X} TABLE;
X
X
X/*
X** Daylight-savings mode: on, off, or not yet known.
X*/
Xtypedef enum _DSTMODE {
X DSTon, DSToff, DSTmaybe
X} DSTMODE;
X
X/*
X** Meridian: am, pm, or 24-hour style.
X*/
Xtypedef enum _MERIDIAN {
X MERam, MERpm, MER24
X} MERIDIAN;
X
X
X/*
X** Global variables. We could get rid of most of these by using a good
X** union as the yacc stack. (This routine was originally written before
X** yacc had the %union construct.) Maybe someday; right now we only use
X** the %union very rarely.
X*/
Xstatic char *yyInput;
Xstatic DSTMODE yyDSTmode;
Xstatic time_t yyDayOrdinal;
Xstatic time_t yyDayNumber;
Xstatic int yyHaveDate;
Xstatic int yyHaveDay;
Xstatic int yyHaveRel;
Xstatic int yyHaveTime;
Xstatic int yyHaveZone;
Xstatic time_t yyTimezone;
Xstatic time_t yyDay;
Xstatic time_t yyHour;
Xstatic time_t yyMinutes;
Xstatic time_t yyMonth;
Xstatic time_t yySeconds;
Xstatic time_t yyYear;
Xstatic MERIDIAN yyMeridian;
Xstatic time_t yyRelMonth;
Xstatic time_t yyRelSeconds;
X
X
X#line 163 "./getdate.y"
Xtypedef union {
X time_t Number;
X enum _MERIDIAN Meridian;
X} YYSTYPE;
X
X#ifndef YYLTYPE
Xtypedef
X struct yyltype
X {
X int timestamp;
X int first_line;
X int first_column;
X int last_line;
X int last_column;
X char *text;
X }
X yyltype;
X
X#define YYLTYPE yyltype
X#endif
X
X#include <stdio.h>
X
X#ifndef __STDC__
X#define const
X#endif
X
X
X
X#define YYFINAL 51
X#define YYFLAG -32768
X#define YYNTBASE 19
X
X#define YYTRANSLATE(x) ((unsigned)(x) <= 270 ? yytranslate[x] : 29)
X
Xstatic const char yytranslate[] = { 0,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 17, 2, 2, 18, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 16, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
X 2, 2, 2, 2, 2, 1, 2, 3, 4, 5,
X 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
X};
X
Xstatic const short yyprhs[] = { 0,
X 0, 1, 4, 6, 8, 10, 12, 14, 16, 19,
X 24, 29, 36, 43, 45, 47, 50, 52, 55, 58,
X 62, 68, 72, 75, 80, 83, 87, 90, 92, 95,
X 98, 100, 103, 106, 108, 111, 114, 116, 118, 119
X};
X
Xstatic const short yyrhs[] = { -1,
X 19, 20, 0, 21, 0, 22, 0, 24, 0, 23,
X 0, 25, 0, 27, 0, 13, 7, 0, 13, 16,
X 13, 28, 0, 13, 16, 13, 12, 0, 13, 16,
X 13, 16, 13, 28, 0, 13, 16, 13, 16, 13,
X 12, 0, 14, 0, 5, 0, 14, 15, 0, 4,
X 0, 4, 17, 0, 13, 4, 0, 13, 18, 13,
X 0, 13, 18, 13, 18, 13, 0, 13, 12, 12,
X 0, 9, 13, 0, 9, 13, 17, 13, 0, 13,
X 9, 0, 13, 9, 13, 0, 26, 3, 0, 26,
X 0, 13, 8, 0, 12, 8, 0, 8, 0, 12,
X 11, 0, 13, 11, 0, 11, 0, 12, 10, 0,
X 13, 10, 0, 10, 0, 13, 0, 0, 7, 0
X};
X
X#if YYDEBUG != 0
Xstatic const short yyrline[] = { 0,
X 177, 178, 181, 184, 187, 190, 193, 196, 199, 205,
X 211, 218, 224, 234, 238, 242, 249, 253, 257, 263,
X 267, 272, 278, 282, 287, 291, 298, 302, 305, 308,
X 311, 314, 317, 320, 323, 326, 329, 334, 362, 365
X};
X
Xstatic const char * const yytname[] = { "$","error","$illegal.","tAGO","tDAY",
X"tDAYZONE","tID","tMERIDIAN","tMINUTE_UNIT","tMONTH","tMONTH_UNIT","tSEC_UNIT",
X"tSNUMBER","tUNUMBER","tZONE","tDST","':'","','","'/'","spec","item","time",
X"zone","day","date","rel","relunit","number","o_merid",""
X};
X#endif
X
Xstatic const short yyr1[] = { 0,
X 19, 19, 20, 20, 20, 20, 20, 20, 21, 21,
X 21, 21, 21, 22, 22, 22, 23, 23, 23, 24,
X 24, 24, 24, 24, 24, 24, 25, 25, 26, 26,
X 26, 26, 26, 26, 26, 26, 26, 27, 28, 28
X};
X
Xstatic const short yyr2[] = { 0,
X 0, 2, 1, 1, 1, 1, 1, 1, 2, 4,
X 4, 6, 6, 1, 1, 2, 1, 2, 2, 3,
X 5, 3, 2, 4, 2, 3, 2, 1, 2, 2,
X 1, 2, 2, 1, 2, 2, 1, 1, 0, 1
X};
X
Xstatic const short yydefact[] = { 1,
X 0, 17, 15, 31, 0, 37, 34, 0, 38, 14,
X 2, 3, 4, 6, 5, 7, 28, 8, 18, 23,
X 30, 35, 32, 19, 9, 29, 25, 36, 33, 0,
X 0, 0, 16, 27, 0, 26, 22, 39, 20, 24,
X 40, 11, 0, 10, 0, 39, 21, 13, 12, 0,
X 0
X};
X
Xstatic const short yydefgoto[] = { 1,
X 11, 12, 13, 14, 15, 16, 17, 18, 44
X};
X
Xstatic const short yypact[] = {-32768,
X 0, -15,-32768,-32768, -10,-32768,-32768, 25, 11, -8,
X-32768,-32768,-32768,-32768,-32768,-32768, 13,-32768,-32768, 7,
X-32768,-32768,-32768,-32768,-32768,-32768, 4,-32768,-32768, 14,
X 15, 19,-32768,-32768, 24,-32768,-32768, 18, 20,-32768,
X-32768,-32768, 26,-32768, 27, -6,-32768,-32768,-32768, 31,
X-32768
X};
X
Xstatic const short yypgoto[] = {-32768,
X-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, -5
X};
X
X
X#define YYLAST 41
X
X
Xstatic const short yytable[] = { 50,
X 41, 19, 20, 2, 3, 48, 33, 4, 5, 6,
X 7, 8, 9, 10, 24, 34, 36, 25, 26, 27,
X 28, 29, 30, 35, 41, 37, 31, 38, 32, 42,
X 51, 39, 21, 43, 22, 23, 40, 45, 46, 47,
X 49
X};
X
Xstatic const short yycheck[] = { 0,
X 7, 17, 13, 4, 5, 12, 15, 8, 9, 10,
X 11, 12, 13, 14, 4, 3, 13, 7, 8, 9,
X 10, 11, 12, 17, 7, 12, 16, 13, 18, 12,
X 0, 13, 8, 16, 10, 11, 13, 18, 13, 13,
X 46
X};
X/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
X#line 3 "bison.simple"
X
X/* Skeleton output parser for bison,
X Copyright (C) 1984, 1989, 1990 Bob Corbett and Richard Stallman
X
X This program is free software; you can redistribute it and/or modify
X it under the terms of the GNU General Public License as published by
X the Free Software Foundation; either version 1, or (at your option)
X any later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X
X#ifndef alloca
X#ifdef __GNUC__
X#define alloca __builtin_alloca
X#else /* not GNU C. */
X#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__)
X#include <alloca.h>
X#else /* not sparc */
X#if defined (MSDOS) && !defined (__TURBOC__)
X#include <malloc.h>
X#else /* not MSDOS, or __TURBOC__ */
X#if defined(_AIX)
X#include <malloc.h>
X #pragma alloca
X#endif /* not _AIX */
X#endif /* not MSDOS, or __TURBOC__ */
X#endif /* not sparc. */
X#endif /* not GNU C. */
X#endif /* alloca not defined. */
X
X/* This is the parser code that is written into each bison parser
X when the %semantic_parser declaration is not specified in the grammar.
X It was written by Richard Stallman by simplifying the hairy parser
X used when %semantic_parser is specified. */
X
X/* Note: there must be only one dollar sign in this file.
X It is replaced by the list of actions, each action
X as one case of the switch. */
X
X#define yyerrok (yyerrstatus = 0)
X#define yyclearin (yychar = YYEMPTY)
X#define YYEMPTY -2
X#define YYEOF 0
X#define YYACCEPT return(0)
X#define YYABORT return(1)
X#define YYERROR goto yyerrlab1
X/* Like YYERROR except do call yyerror.
X This remains here temporarily to ease the
X transition to the new meaning of YYERROR, for GCC.
X Once GCC version 2 has supplanted version 1, this can go. */
X#define YYFAIL goto yyerrlab
X#define YYRECOVERING() (!!yyerrstatus)
X#define YYBACKUP(token, value) \
Xdo \
X if (yychar == YYEMPTY && yylen == 1) \
X { yychar = (token), yylval = (value); \
X yychar1 = YYTRANSLATE (yychar); \
X YYPOPSTACK; \
X goto yybackup; \
X } \
X else \
X { yyerror ("syntax error: cannot back up"); YYERROR; } \
Xwhile (0)
X
X#define YYTERROR 1
X#define YYERRCODE 256
X
X#ifndef YYPURE
X#define YYLEX yylex()
X#endif
X
X#ifdef YYPURE
X#ifdef YYLSP_NEEDED
X#define YYLEX yylex(&yylval, &yylloc)
X#else
X#define YYLEX yylex(&yylval)
X#endif
X#endif
X
X/* If nonreentrant, generate the variables here */
X
X#ifndef YYPURE
X
Xint yychar; /* the lookahead symbol */
XYYSTYPE yylval; /* the semantic value of the */
X /* lookahead symbol */
X
X#ifdef YYLSP_NEEDED
XYYLTYPE yylloc; /* location data for the lookahead */
X /* symbol */
X#endif
X
Xint yynerrs; /* number of parse errors so far */
X#endif /* not YYPURE */
X
X#if YYDEBUG != 0
Xint yydebug; /* nonzero means print parse trace */
X/* Since this is uninitialized, it does not stop multiple parsers
X from coexisting. */
X#endif
X
X/* YYINITDEPTH indicates the initial size of the parser's stacks */
X
X#ifndef YYINITDEPTH
X#define YYINITDEPTH 200
X#endif
X
X/* YYMAXDEPTH is the maximum size the stacks can grow to
X (effective only if the built-in stack extension method is used). */
X
X#if YYMAXDEPTH == 0
X#undef YYMAXDEPTH
X#endif
X
X#ifndef YYMAXDEPTH
X#define YYMAXDEPTH 10000
X#endif
X
X#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
X#define __yy_bcopy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
X#else /* not GNU C or C++ */
X#ifndef __cplusplus
X
X/* This is the most reliable way to avoid incompatibilities
X in available built-in functions on various systems. */
Xstatic void
X__yy_bcopy (from, to, count)
X char *from;
X char *to;
X int count;
X{
X register char *f = from;
X register char *t = to;
X register int i = count;
X
X while (i-- > 0)
X *t++ = *f++;
X}
X
X#else /* __cplusplus */
X
X/* This is the most reliable way to avoid incompatibilities
X in available built-in functions on various systems. */
Xstatic void
X__yy_bcopy (char *from, char *to, int count)
X{
X register char *f = from;
X register char *t = to;
X register int i = count;
X
X while (i-- > 0)
X *t++ = *f++;
X}
X
X#endif
X#endif
X
X#line 169 "bison.simple"
Xint
Xyyparse()
X{
X register int yystate;
X register int yyn;
X register short *yyssp;
X register YYSTYPE *yyvsp;
X int yyerrstatus; /* number of tokens to shift before error messages enabled */
X int yychar1; /* lookahead token as an internal (translated) token number */
X
X short yyssa[YYINITDEPTH]; /* the state stack */
X YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
X
X short *yyss = yyssa; /* refer to the stacks thru separate pointers */
X YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
X
X#ifdef YYLSP_NEEDED
X YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
X YYLTYPE *yyls = yylsa;
X YYLTYPE *yylsp;
X
X#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
X#else
X#define YYPOPSTACK (yyvsp--, yyssp--)
X#endif
X
X int yystacksize = YYINITDEPTH;
X
X#ifdef YYPURE
X int yychar;
X YYSTYPE yylval;
X int yynerrs;
X#ifdef YYLSP_NEEDED
X YYLTYPE yylloc;
X#endif
X#endif
X
X YYSTYPE yyval; /* the variable used to return */
X /* semantic values from the action */
X /* routines */
X
X int yylen;
X
X#if YYDEBUG != 0
X if (yydebug)
X fprintf(stderr, "Starting parse\n");
X#endif
X
X yystate = 0;
X yyerrstatus = 0;
X yynerrs = 0;
X yychar = YYEMPTY; /* Cause a token to be read. */
X
X /* Initialize stack pointers.
X Waste one element of value and location stack
X so that they stay on the same level as the state stack. */
X
X yyssp = yyss - 1;
X yyvsp = yyvs;
X#ifdef YYLSP_NEEDED
X yylsp = yyls;
X#endif
X
X/* Push a new state, which is found in yystate . */
X/* In all cases, when you get here, the value and location stacks
X have just been pushed. so pushing a state here evens the stacks. */
Xyynewstate:
X
X *++yyssp = yystate;
X
X if (yyssp >= yyss + yystacksize - 1)
X {
X /* Give user a chance to reallocate the stack */
X /* Use copies of these so that the &'s don't force the real ones into memory. */
X YYSTYPE *yyvs1 = yyvs;
X short *yyss1 = yyss;
X#ifdef YYLSP_NEEDED
X YYLTYPE *yyls1 = yyls;
X#endif
X
X /* Get the current used size of the three stacks, in elements. */
X int size = yyssp - yyss + 1;
X
X#ifdef yyoverflow
X /* Each stack pointer address is followed by the size of
X the data in use in that stack, in bytes. */
X yyoverflow("parser stack overflow",
X &yyss1, size * sizeof (*yyssp),
X &yyvs1, size * sizeof (*yyvsp),
X#ifdef YYLSP_NEEDED
X &yyls1, size * sizeof (*yylsp),
X#endif
X &yystacksize);
X
X yyss = yyss1; yyvs = yyvs1;
X#ifdef YYLSP_NEEDED
X yyls = yyls1;
X#endif
X#else /* no yyoverflow */
X /* Extend the stack our own way. */
X if (yystacksize >= YYMAXDEPTH)
X {
X yyerror("parser stack overflow");
X return 2;
X }
X yystacksize *= 2;
X if (yystacksize > YYMAXDEPTH)
X yystacksize = YYMAXDEPTH;
X yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
X __yy_bcopy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
X yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
X __yy_bcopy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
X#ifdef YYLSP_NEEDED
X yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
X __yy_bcopy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
X#endif
X#endif /* no yyoverflow */
X
X yyssp = yyss + size - 1;
X yyvsp = yyvs + size - 1;
X#ifdef YYLSP_NEEDED
X yylsp = yyls + size - 1;
X#endif
X
X#if YYDEBUG != 0
X if (yydebug)
X fprintf(stderr, "Stack size increased to %d\n", yystacksize);
X#endif
X
X if (yyssp >= yyss + yystacksize - 1)
X YYABORT;
X }
X
X#if YYDEBUG != 0
X if (yydebug)
X fprintf(stderr, "Entering state %d\n", yystate);
X#endif
X
X yybackup:
X
X/* Do appropriate processing given the current state. */
X/* Read a lookahead token if we need one and don't already have one. */
X/* yyresume: */
X
X /* First try to decide what to do without reference to lookahead token. */
X
X yyn = yypact[yystate];
X if (yyn == YYFLAG)
X goto yydefault;
X
X /* Not known => get a lookahead token if don't already have one. */
X
X /* yychar is either YYEMPTY or YYEOF
X or a valid token in external form. */
X
X if (yychar == YYEMPTY)
X {
X#if YYDEBUG != 0
X if (yydebug)
X fprintf(stderr, "Reading a token: ");
X#endif
X yychar = YYLEX;
X }
X
X /* Convert token to internal form (in yychar1) for indexing tables with */
X
X if (yychar <= 0) /* This means end of input. */
X {
X yychar1 = 0;
X yychar = YYEOF; /* Don't call YYLEX any more */
X
X#if YYDEBUG != 0
X if (yydebug)
X fprintf(stderr, "Now at end of input.\n");
X#endif
X }
X else
X {
X yychar1 = YYTRANSLATE(yychar);
X
X#if YYDEBUG != 0
X if (yydebug)
X {
X fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
X /* Give the individual parser a way to print the precise meaning
X of a token, for further debugging info. */
X#ifdef YYPRINT
X YYPRINT (stderr, yychar, yylval);
X#endif
X fprintf (stderr, ")\n");
X }
X#endif
X }
X
X yyn += yychar1;
X if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
X goto yydefault;
X
X yyn = yytable[yyn];
X
X /* yyn is what to do for this token type in this state.
X Negative => reduce, -yyn is rule number.
X Positive => shift, yyn is new state.
X New state is final state => don't bother to shift,
X just return success.
X 0, or most negative number => error. */
X
X if (yyn < 0)
X {
X if (yyn == YYFLAG)
X goto yyerrlab;
X yyn = -yyn;
X goto yyreduce;
X }
X else if (yyn == 0)
X goto yyerrlab;
X
X if (yyn == YYFINAL)
X YYACCEPT;
X
X /* Shift the lookahead token. */
X
X#if YYDEBUG != 0
X if (yydebug)
X fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
X#endif
X
X /* Discard the token being shifted unless it is eof. */
X if (yychar != YYEOF)
X yychar = YYEMPTY;
X
X *++yyvsp = yylval;
X#ifdef YYLSP_NEEDED
X *++yylsp = yylloc;
X#endif
X
X /* count tokens shifted since error; after three, turn off error status. */
X if (yyerrstatus) yyerrstatus--;
X
X yystate = yyn;
X goto yynewstate;
X
X/* Do the default action for the current state. */
Xyydefault:
X
X yyn = yydefact[yystate];
X if (yyn == 0)
X goto yyerrlab;
X
X/* Do a reduction. yyn is the number of a rule to reduce with. */
Xyyreduce:
X yylen = yyr2[yyn];
X yyval = yyvsp[1-yylen]; /* implement default value of the action */
X
X#if YYDEBUG != 0
X if (yydebug)
X {
X int i;
X
X fprintf (stderr, "Reducing via rule %d (line %d), ",
X yyn, yyrline[yyn]);
X
X /* Print the symboles being reduced, and their result. */
X for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
X fprintf (stderr, "%s ", yytname[yyrhs[i]]);
X fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
X }
X#endif
X
X
X switch (yyn) {
X
Xcase 3:
X#line 181 "./getdate.y"
X{
X yyHaveTime++;
X ;
X break;}
Xcase 4:
X#line 184 "./getdate.y"
X{
X yyHaveZone++;
X ;
X break;}
Xcase 5:
X#line 187 "./getdate.y"
X{
X yyHaveDate++;
X ;
X break;}
Xcase 6:
X#line 190 "./getdate.y"
X{
X yyHaveDay++;
X ;
X break;}
Xcase 7:
X#line 193 "./getdate.y"
X{
X yyHaveRel++;
X ;
X break;}
Xcase 9:
X#line 199 "./getdate.y"
X{
X yyHour = yyvsp[-1].Number;
X yyMinutes = 0;
X yySeconds = 0;
X yyMeridian = yyvsp[0].Meridian;
X ;
X break;}
Xcase 10:
X#line 205 "./getdate.y"
X{
X yyHour = yyvsp[-3].Number;
X yyMinutes = yyvsp[-1].Number;
X yySeconds = 0;
X yyMeridian = yyvsp[0].Meridian;
X ;
X break;}
Xcase 11:
X#line 211 "./getdate.y"
X{
X yyHour = yyvsp[-3].Number;
X yyMinutes = yyvsp[-1].Number;
X yyMeridian = MER24;
X yyDSTmode = DSToff;
X yyTimezone = - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60);
X ;
X break;}
Xcase 12:
X#line 218 "./getdate.y"
X{
X yyHour = yyvsp[-5].Number;
X yyMinutes = yyvsp[-3].Number;
X yySeconds = yyvsp[-1].Number;
X yyMeridian = yyvsp[0].Meridian;
X ;
X break;}
Xcase 13:
X#line 224 "./getdate.y"
X{
X yyHour = yyvsp[-5].Number;
X yyMinutes = yyvsp[-3].Number;
X yySeconds = yyvsp[-1].Number;
X yyMeridian = MER24;
X yyDSTmode = DSToff;
X yyTimezone = - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60);
X ;
X break;}
Xcase 14:
X#line 234 "./getdate.y"
X{
X yyTimezone = yyvsp[0].Number;
X yyDSTmode = DSToff;
X ;
X break;}
Xcase 15:
X#line 238 "./getdate.y"
X{
X yyTimezone = yyvsp[0].Number;
X yyDSTmode = DSTon;
X ;
X break;}
Xcase 16:
X#line 243 "./getdate.y"
X{
X yyTimezone = yyvsp[-1].Number;
X yyDSTmode = DSTon;
X ;
X break;}
Xcase 17:
X#line 249 "./getdate.y"
X{
X yyDayOrdinal = 1;
X yyDayNumber = yyvsp[0].Number;
X ;
X break;}
Xcase 18:
X#line 253 "./getdate.y"
X{
X yyDayOrdinal = 1;
X yyDayNumber = yyvsp[-1].Number;
X ;
X break;}
Xcase 19:
X#line 257 "./getdate.y"
X{
X yyDayOrdinal = yyvsp[-1].Number;
X yyDayNumber = yyvsp[0].Number;
X ;
X break;}
Xcase 20:
X#line 263 "./getdate.y"
X{
X yyMonth = yyvsp[-2].Number;
X yyDay = yyvsp[0].Number;
X ;
X break;}
Xcase 21:
X#line 267 "./getdate.y"
X{
X yyMonth = yyvsp[-4].Number;
X yyDay = yyvsp[-2].Number;
X yyYear = yyvsp[0].Number;
X ;
X break;}
Xcase 22:
X#line 272 "./getdate.y"
X{
X /* ISO 8601 format. yyyy-mm-dd. */
X yyYear = yyvsp[-2].Number;
X yyMonth = -yyvsp[-1].Number;
X yyDay = -yyvsp[0].Number;
X ;
X break;}
Xcase 23:
X#line 278 "./getdate.y"
X{
X yyMonth = yyvsp[-1].Number;
X yyDay = yyvsp[0].Number;
X ;
X break;}
Xcase 24:
X#line 282 "./getdate.y"
X{
X yyMonth = yyvsp[-3].Number;
X yyDay = yyvsp[-2].Number;
X yyYear = yyvsp[0].Number;
X ;
X break;}
Xcase 25:
X#line 287 "./getdate.y"
X{
X yyMonth = yyvsp[0].Number;
X yyDay = yyvsp[-1].Number;
X ;
X break;}
Xcase 26:
X#line 291 "./getdate.y"
X{
X yyMonth = yyvsp[-1].Number;
X yyDay = yyvsp[-2].Number;
X yyYear = yyvsp[0].Number;
X ;
X break;}
Xcase 27:
X#line 298 "./getdate.y"
X{
X yyRelSeconds = -yyRelSeconds;
X yyRelMonth = -yyRelMonth;
X ;
X break;}
Xcase 29:
X#line 305 "./getdate.y"
X{
X yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number * 60L;
X ;
X break;}
Xcase 30:
X#line 308 "./getdate.y"
X{
X yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number * 60L;
X ;
X break;}
Xcase 31:
X#line 311 "./getdate.y"
X{
X yyRelSeconds += yyvsp[0].Number * 60L;
X ;
X break;}
Xcase 32:
X#line 314 "./getdate.y"
X{
X yyRelSeconds += yyvsp[-1].Number;
X ;
X break;}
Xcase 33:
X#line 317 "./getdate.y"
X{
X yyRelSeconds += yyvsp[-1].Number;
X ;
X break;}
Xcase 34:
X#line 320 "./getdate.y"
X{
X yyRelSeconds++;
X ;
X break;}
Xcase 35:
X#line 323 "./getdate.y"
X{
X yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number;
X ;
X break;}
Xcase 36:
X#line 326 "./getdate.y"
X{
X yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number;
X ;
X break;}
Xcase 37:
X#line 329 "./getdate.y"
X{
X yyRelMonth += yyvsp[0].Number;
X ;
X break;}
Xcase 38:
X#line 334 "./getdate.y"
X{
X if (yyHaveTime && yyHaveDate && !yyHaveRel)
X yyYear = yyvsp[0].Number;
X else {
X if(yyvsp[0].Number>10000) {
X time_t date_part;
X
X date_part= yyvsp[0].Number/10000;
X yyHaveDate++;
X yyDay= (date_part)%100;
X yyMonth= (date_part/100)%100;
X yyYear = date_part/10000;
X }
X yyHaveTime++;
X if (yyvsp[0].Number < 100) {
X yyHour = yyvsp[0].Number;
X yyMinutes = 0;
X }
X else {
X yyHour = yyvsp[0].Number / 100;
X yyMinutes = yyvsp[0].Number % 100;
X }
X yySeconds = 0;
X yyMeridian = MER24;
X }
X ;
X break;}
Xcase 39:
X#line 362 "./getdate.y"
X{
X yyval.Meridian = MER24;
X ;
X break;}
Xcase 40:
X#line 365 "./getdate.y"
X{
X yyval.Meridian = yyvsp[0].Meridian;
X ;
X break;}
X}
X /* the action file gets copied in in place of this dollarsign */
X#line 440 "bison.simple"
X
X yyvsp -= yylen;
X yyssp -= yylen;
X#ifdef YYLSP_NEEDED
X yylsp -= yylen;
X#endif
X
X#if YYDEBUG != 0
X if (yydebug)
X {
X short *ssp1 = yyss - 1;
X fprintf (stderr, "state stack now");
X while (ssp1 != yyssp)
X fprintf (stderr, " %d", *++ssp1);
X fprintf (stderr, "\n");
X }
X#endif
X
X *++yyvsp = yyval;
X
X#ifdef YYLSP_NEEDED
X yylsp++;
X if (yylen == 0)
X {
X yylsp->first_line = yylloc.first_line;
X yylsp->first_column = yylloc.first_column;
X yylsp->last_line = (yylsp-1)->last_line;
X yylsp->last_column = (yylsp-1)->last_column;
X yylsp->text = 0;
X }
X else
X {
X yylsp->last_line = (yylsp+yylen-1)->last_line;
X yylsp->last_column = (yylsp+yylen-1)->last_column;
X }
X#endif
X
X /* Now "shift" the result of the reduction.
X Determine what state that goes to,
X based on the state we popped back to
X and the rule number reduced by. */
X
X yyn = yyr1[yyn];
X
X yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
X if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
X yystate = yytable[yystate];
X else
X yystate = yydefgoto[yyn - YYNTBASE];
X
X goto yynewstate;
X
Xyyerrlab: /* here on detecting error */
X
X if (! yyerrstatus)
X /* If not already recovering from an error, report this error. */
X {
X ++yynerrs;
X
X#ifdef YYERROR_VERBOSE
X yyn = yypact[yystate];
X
X if (yyn > YYFLAG && yyn < YYLAST)
X {
X int size = 0;
X char *msg;
X int x, count;
X
X count = 0;
X for (x = 0; x < (sizeof(yytname) / sizeof(char *)); x++)
X if (yycheck[x + yyn] == x)
X size += strlen(yytname[x]) + 15, count++;
X msg = (char *) malloc(size + 15);
X if (msg != 0)
X {
X strcpy(msg, "parse error");
X
X if (count < 5)
X {
X count = 0;
X for (x = 0; x < (sizeof(yytname) / sizeof(char *)); x++)
X if (yycheck[x + yyn] == x)
X {
X strcat(msg, count == 0 ? ", expecting `" : " or `");
X strcat(msg, yytname[x]);
X strcat(msg, "'");
X count++;
X }
X }
X yyerror(msg);
X free(msg);
X }
X else
X yyerror ("parse error; also virtual memory exceeded");
X }
X else
X#endif /* YYERROR_VERBOSE */
X yyerror("parse error");
X }
X
Xyyerrlab1: /* here on error raised explicitly by an action */
X
X if (yyerrstatus == 3)
X {
X /* if just tried and failed to reuse lookahead token after an error, discard it. */
X
X /* return failure if at end of input */
X if (yychar == YYEOF)
X YYABORT;
X
X#if YYDEBUG != 0
X if (yydebug)
X fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
X#endif
X
X yychar = YYEMPTY;
X }
X
X /* Else will try to reuse lookahead token
X after shifting the error token. */
X
X yyerrstatus = 3; /* Each real token shifted decrements this */
X
X goto yyerrhandle;
X
Xyyerrdefault: /* current state does not do anything special for the error token. */
X
X#if 0
X /* This is wrong; only states that explicitly want error tokens
X should shift them. */
X yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
X if (yyn) goto yydefault;
X#endif
X
Xyyerrpop: /* pop the current state because it cannot handle the error token */
X
X if (yyssp == yyss) YYABORT;
X yyvsp--;
X yystate = *--yyssp;
X#ifdef YYLSP_NEEDED
X yylsp--;
X#endif
X
X#if YYDEBUG != 0
X if (yydebug)
X {
X short *ssp1 = yyss - 1;
X fprintf (stderr, "Error: state stack now");
X while (ssp1 != yyssp)
X fprintf (stderr, " %d", *++ssp1);
X fprintf (stderr, "\n");
X }
X#endif
X
Xyyerrhandle:
X
X yyn = yypact[yystate];
X if (yyn == YYFLAG)
X goto yyerrdefault;
X
X yyn += YYTERROR;
X if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
X goto yyerrdefault;
X
X yyn = yytable[yyn];
X if (yyn < 0)
X {
X if (yyn == YYFLAG)
X goto yyerrpop;
X yyn = -yyn;
X goto yyreduce;
X }
X else if (yyn == 0)
X goto yyerrpop;
X
X if (yyn == YYFINAL)
X YYACCEPT;
X
X#if YYDEBUG != 0
X if (yydebug)
X fprintf(stderr, "Shifting error token, ");
X#endif
X
X *++yyvsp = yylval;
X#ifdef YYLSP_NEEDED
X *++yylsp = yylloc;
X#endif
X
X yystate = yyn;
X goto yynewstate;
X}
X#line 370 "./getdate.y"
X
X
X/* Month and day table. */
Xstatic TABLE const MonthDayTable[] = {
X { "january", tMONTH, 1 },
X { "february", tMONTH, 2 },
X { "march", tMONTH, 3 },
X { "april", tMONTH, 4 },
X { "may", tMONTH, 5 },
X { "june", tMONTH, 6 },
X { "july", tMONTH, 7 },
X { "august", tMONTH, 8 },
X { "september", tMONTH, 9 },
X { "sept", tMONTH, 9 },
X { "october", tMONTH, 10 },
X { "november", tMONTH, 11 },
X { "december", tMONTH, 12 },
X { "sunday", tDAY, 0 },
X { "monday", tDAY, 1 },
X { "tuesday", tDAY, 2 },
X { "tues", tDAY, 2 },
X { "wednesday", tDAY, 3 },
X { "wednes", tDAY, 3 },
X { "thursday", tDAY, 4 },
X { "thur", tDAY, 4 },
X { "thurs", tDAY, 4 },
X { "friday", tDAY, 5 },
X { "saturday", tDAY, 6 },
X { NULL }
X};
X
X/* Time units table. */
Xstatic TABLE const UnitsTable[] = {
X { "year", tMONTH_UNIT, 12 },
X { "month", tMONTH_UNIT, 1 },
X { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
X { "week", tMINUTE_UNIT, 7 * 24 * 60 },
X { "day", tMINUTE_UNIT, 1 * 24 * 60 },
X { "hour", tMINUTE_UNIT, 60 },
X { "minute", tMINUTE_UNIT, 1 },
X { "min", tMINUTE_UNIT, 1 },
X { "second", tSEC_UNIT, 1 },
X { "sec", tSEC_UNIT, 1 },
X { NULL }
X};
X
X/* Assorted relative-time words. */
Xstatic TABLE const OtherTable[] = {
X { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
X { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
X { "today", tMINUTE_UNIT, 0 },
X { "now", tMINUTE_UNIT, 0 },
X { "last", tUNUMBER, -1 },
X { "this", tMINUTE_UNIT, 0 },
X { "next", tUNUMBER, 2 },
X { "first", tUNUMBER, 1 },
X/* { "second", tUNUMBER, 2 }, */
X { "third", tUNUMBER, 3 },
X { "fourth", tUNUMBER, 4 },
X { "fifth", tUNUMBER, 5 },
X { "sixth", tUNUMBER, 6 },
X { "seventh", tUNUMBER, 7 },
X { "eighth", tUNUMBER, 8 },
X { "ninth", tUNUMBER, 9 },
X { "tenth", tUNUMBER, 10 },
X { "eleventh", tUNUMBER, 11 },
X { "twelfth", tUNUMBER, 12 },
X { "ago", tAGO, 1 },
X { NULL }
X};
X
X/* The timezone table. */
X/* Some of these are commented out because a time_t can't store a float. */
Xstatic TABLE const TimezoneTable[] = {
X { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
X { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
X { "utc", tZONE, HOUR( 0) },
X { "wet", tZONE, HOUR( 0) }, /* Western European */
X { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
X { "wat", tZONE, HOUR( 1) }, /* West Africa */
X { "at", tZONE, HOUR( 2) }, /* Azores */
X#if 0
X /* For completeness. BST is also British Summer, and GST is
X * also Guam Standard. */
X { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
X { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
X#endif
X#if 0
X { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
X { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
X { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
X#endif
X { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
X { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
X { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
X { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
X { "cst", tZONE, HOUR( 6) }, /* Central Standard */
X { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
X { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
X { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
X { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
X { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
X { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
X { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
X { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
X { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
X { "cat", tZONE, HOUR(10) }, /* Central Alaska */
X { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
X { "nt", tZONE, HOUR(11) }, /* Nome */
X { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
X { "cet", tZONE, -HOUR(1) }, /* Central European */
X { "met", tZONE, -HOUR(1) }, /* Middle European */
X { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
X { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
X { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
X { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
X { "fwt", tZONE, -HOUR(1) }, /* French Winter */
X { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
X { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
X { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
X#if 0
X { "it", tZONE, -HOUR(3.5) },/* Iran */
X#endif
X { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
X { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
X#if 0
X { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
X#endif
X { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
X#if 0
X /* For completeness. NST is also Newfoundland Stanard, and SST is
X * also Swedish Summer. */
X { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
X { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
X#endif /* 0 */
X { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
X { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
X#if 0
X { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
X#endif
X { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
X { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
X#if 0
X { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
X { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
X#endif
X { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
X { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
X { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
X { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
X { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
X { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
X { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
X { NULL }
X};
X
X/* Military timezone table. */
Xstatic TABLE const MilitaryTable[] = {
X { "a", tZONE, HOUR( 1) },
X { "b", tZONE, HOUR( 2) },
X { "c", tZONE, HOUR( 3) },
X { "d", tZONE, HOUR( 4) },
X { "e", tZONE, HOUR( 5) },
X { "f", tZONE, HOUR( 6) },
X { "g", tZONE, HOUR( 7) },
X { "h", tZONE, HOUR( 8) },
X { "i", tZONE, HOUR( 9) },
X { "k", tZONE, HOUR( 10) },
X { "l", tZONE, HOUR( 11) },
X { "m", tZONE, HOUR( 12) },
X { "n", tZONE, HOUR(- 1) },
X { "o", tZONE, HOUR(- 2) },
X { "p", tZONE, HOUR(- 3) },
X { "q", tZONE, HOUR(- 4) },
X { "r", tZONE, HOUR(- 5) },
X { "s", tZONE, HOUR(- 6) },
X { "t", tZONE, HOUR(- 7) },
X { "u", tZONE, HOUR(- 8) },
X { "v", tZONE, HOUR(- 9) },
X { "w", tZONE, HOUR(-10) },
X { "x", tZONE, HOUR(-11) },
X { "y", tZONE, HOUR(-12) },
X { "z", tZONE, HOUR( 0) },
X { NULL }
X};
X
X
X
X
X/* ARGSUSED */
Xstatic int
Xyyerror(s)
X char *s;
X{
X return 0;
X}
X
X
Xstatic time_t
XToSeconds(Hours, Minutes, Seconds, Meridian)
X time_t Hours;
X time_t Minutes;
X time_t Seconds;
X MERIDIAN Meridian;
X{
X if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
X return -1;
X switch (Meridian) {
X case MER24:
X if (Hours < 0 || Hours > 23)
X return -1;
X return (Hours * 60L + Minutes) * 60L + Seconds;
X case MERam:
X if (Hours < 1 || Hours > 12)
X return -1;
X return (Hours * 60L + Minutes) * 60L + Seconds;
X case MERpm:
X if (Hours < 1 || Hours > 12)
X return -1;
X return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
X }
X /* NOTREACHED */
X}
X
X
Xstatic time_t
XConvert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
X time_t Month;
X time_t Day;
X time_t Year;
X time_t Hours;
X time_t Minutes;
X time_t Seconds;
X MERIDIAN Meridian;
X DSTMODE DSTmode;
X{
X static int DaysInMonth[12] = {
X 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X };
X time_t tod;
X time_t Julian;
X int i;
X
X if (Year < 0)
X Year = -Year;
X if (Year < 100)
X Year += 1900;
X DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
X ? 29 : 28;
X if (Year < EPOCH || Year > 1999
X || Month < 1 || Month > 12
X /* Lint fluff: "conversion from long may lose accuracy" */
X || Day < 1 || Day > DaysInMonth[(int)--Month])
X return -1;
X
X for (Julian = Day - 1, i = 0; i < Month; i++)
X Julian += DaysInMonth[i];
X for (i = EPOCH; i < Year; i++)
X Julian += 365 + (i % 4 == 0);
X Julian *= SECSPERDAY;
X Julian += yyTimezone * 60L;
X if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
X return -1;
X Julian += tod;
X if (DSTmode == DSTon
X || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
X Julian -= 60 * 60;
X return Julian;
X}
X
X
Xstatic time_t
XDSTcorrect(Start, Future)
X time_t Start;
X time_t Future;
X{
X time_t StartDay;
X time_t FutureDay;
X
X StartDay = (localtime(&Start)->tm_hour + 1) % 24;
X FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
X return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
X}
X
X
Xstatic time_t
XRelativeDate(Start, DayOrdinal, DayNumber)
X time_t Start;
X time_t DayOrdinal;
X time_t DayNumber;
X{
X struct tm *tm;
X time_t now;
X
X now = Start;
X tm = localtime(&now);
X now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
X now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
X return DSTcorrect(Start, now);
X}
X
X
Xstatic time_t
XRelativeMonth(Start, RelMonth)
X time_t Start;
X time_t RelMonth;
X{
X struct tm *tm;
X time_t Month;
X time_t Year;
X
X if (RelMonth == 0)
X return 0;
X tm = localtime(&Start);
X Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
X Year = Month / 12;
X Month = Month % 12 + 1;
X return DSTcorrect(Start,
X Convert(Month, (time_t)tm->tm_mday, Year,
X (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
X MER24, DSTmaybe));
X}
X
X
Xstatic int
XLookupWord(buff)
X char *buff;
X{
X register char *p;
X register char *q;
X register const TABLE *tp;
X int i;
X int abbrev;
X
X /* Make it lowercase. */
X for (p = buff; *p; p++)
X if (isupper(*p))
X *p = tolower(*p);
X
X if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
X yylval.Meridian = MERam;
X return tMERIDIAN;
X }
X if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
X yylval.Meridian = MERpm;
X return tMERIDIAN;
X }
X
X /* See if we have an abbreviation for a month. */
X if (strlen(buff) == 3)
X abbrev = 1;
X else if (strlen(buff) == 4 && buff[3] == '.') {
X abbrev = 1;
X buff[3] = '\0';
X }
X else
X abbrev = 0;
X
X for (tp = MonthDayTable; tp->name; tp++) {
X if (abbrev) {
X if (strncmp(buff, tp->name, 3) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X }
X else if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X }
X
X for (tp = TimezoneTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X
X if (strcmp(buff, "dst") == 0)
X return tDST;
X
X for (tp = UnitsTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X
X /* Strip off any plural and try the units table again. */
X i = strlen(buff) - 1;
X if (buff[i] == 's') {
X buff[i] = '\0';
X for (tp = UnitsTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X buff[i] = 's'; /* Put back for "this" in OtherTable. */
X }
X
X for (tp = OtherTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X
X /* Military timezones. */
X if (buff[1] == '\0' && isalpha(*buff)) {
X for (tp = MilitaryTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X }
X
X /* Drop out any periods and try the timezone table again. */
X for (i = 0, p = q = buff; *q; q++)
X if (*q != '.')
X *p++ = *q;
X else
X i++;
X *p = '\0';
X if (i)
X for (tp = TimezoneTable; tp->name; tp++)
X if (strcmp(buff, tp->name) == 0) {
X yylval.Number = tp->value;
X return tp->type;
X }
X
X return tID;
X}
X
X
Xstatic int
Xyylex()
X{
X register char c;
X register char *p;
X char buff[20];
X int Count;
X int sign;
X
X for ( ; ; ) {
X while (isspace(*yyInput))
X yyInput++;
X
X if (isdigit(c = *yyInput) || c == '-' || c == '+') {
X if (c == '-' || c == '+') {
X sign = c == '-' ? -1 : 1;
X if (!isdigit(*++yyInput))
X /* skip the '-' sign */
X continue;
X }
X else
X sign = 0;
X for (yylval.Number = 0; isdigit(c = *yyInput++); )
X yylval.Number = 10 * yylval.Number + c - '0';
X yyInput--;
X if (sign < 0)
X yylval.Number = -yylval.Number;
X return sign ? tSNUMBER : tUNUMBER;
X }
X if (isalpha(c)) {
X for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
X if (p < &buff[sizeof buff - 1])
X *p++ = c;
X *p = '\0';
X yyInput--;
X return LookupWord(buff);
X }
X if (c != '(')
X return *yyInput++;
X Count = 0;
X do {
X c = *yyInput++;
X if (c == '\0')
X return c;
X if (c == '(')
X Count++;
X else if (c == ')')
X Count--;
X } while (Count > 0);
X }
X}
X
X
Xtime_t
Xget_date(p, now)
X char *p;
X struct timeb *now;
X{
X struct tm *tm;
X struct timeb ftz;
X time_t Start;
X time_t tod;
X
X yyInput = p;
X if (now == NULL) {
X now = &ftz;
X#if !defined(HAVE_FTIME)
X (void)time(&ftz.time);
X /* Set the timezone global. */
X tzset();
X {
X#if sgi
X ftz.timezone = (int) _timezone / 60;
X#else /* not sgi */
X#ifdef __386BSD__
X ftz.timezone = 0;
X#else /* neither sgi nor 386BSD */
X#if defined (USG)
X extern time_t timezone;
X
X ftz.timezone = (int) timezone / 60;
X#else /* neither sgi nor 386BSD nor USG */
X struct timeval tv;
X struct timezone tz;
X
X gettimeofday (&tv, &tz);
X ftz.timezone = (int) tz.tz_minuteswest;
X#endif /* neither sgi nor 386BSD nor USG */
X#endif /* neither sgi nor 386BSD */
X#endif /* not sgi */
X }
X#else /* HAVE_FTIME */
X (void)ftime(&ftz);
X#endif /* HAVE_FTIME */
X }
X
X tm = localtime(&now->time);
X yyYear = tm->tm_year;
X yyMonth = tm->tm_mon + 1;
X yyDay = tm->tm_mday;
X yyTimezone = now->timezone;
X yyDSTmode = DSTmaybe;
X yyHour = 0;
X yyMinutes = 0;
X yySeconds = 0;
X yyMeridian = MER24;
X yyRelSeconds = 0;
X yyRelMonth = 0;
X yyHaveDate = 0;
X yyHaveDay = 0;
X yyHaveRel = 0;
X yyHaveTime = 0;
X yyHaveZone = 0;
X
X if (yyparse()
X || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
X return -1;
X
X if (yyHaveDate || yyHaveTime || yyHaveDay) {
X Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
X yyMeridian, yyDSTmode);
X if (Start < 0)
X return -1;
X }
X else {
X Start = now->time;
X if (!yyHaveRel)
X Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
X }
X
X Start += yyRelSeconds;
X Start += RelativeMonth(Start, yyRelMonth);
X
X if (yyHaveDay && !yyHaveDate) {
X tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
X Start += tod;
X }
X
X /* Have to do *something* with a legitimate -1 so it's distinguishable
X * from the error return value. (Alternately could set errno on error.) */
X return Start == -1 ? 0 : Start;
X}
X
X
X#if defined(TEST)
X
X/* ARGSUSED */
Xmain(ac, av)
X int ac;
X char *av[];
X{
X char buff[128];
X time_t d;
X
X (void)printf("Enter date, or blank line to exit.\n\t> ");
X (void)fflush(stdout);
X while (gets(buff) && buff[0]) {
X d = get_date(buff, (struct timeb *)NULL);
X if (d == -1)
X (void)printf("Bad format - couldn't convert.\n");
X else
X (void)printf("%s", ctime(&d));
X (void)printf("\t> ");
X (void)fflush(stdout);
X }
X exit(0);
X /* NOTREACHED */
X}
X#endif /* defined(TEST) */
END_OF_FILE
if test 46681 -ne `wc -c <'getdate.c'`; then
echo shar: \"'getdate.c'\" unpacked with wrong size!
fi
# end of 'getdate.c'
fi
if test -f 'alloca.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'alloca.c'\"
else
echo shar: Extracting \"'alloca.c'\" \(5356 characters\)
sed "s/^X//" >'alloca.c' <<'END_OF_FILE'
X/*
X alloca -- (mostly) portable public-domain implementation -- D A Gwyn
X
X last edit: 86/05/30 rms
X include config.h, since on VMS it renames some symbols.
X Use xmalloc instead of malloc.
X
X This implementation of the PWB library alloca() function,
X which is used to allocate space off the run-time stack so
X that it is automatically reclaimed upon procedure exit,
X was inspired by discussions with J. Q. Johnson of Cornell.
X
X It should work under any C implementation that uses an
X actual procedure stack (as opposed to a linked list of
X frames). There are some preprocessor constants that can
X be defined when compiling for your specific system, for
X improved efficiency; however, the defaults should be okay.
X
X The general concept of this implementation is to keep
X track of all alloca()-allocated blocks, and reclaim any
X that are found to be deeper in the stack than the current
X invocation. This heuristic does not reclaim storage as
X soon as it becomes invalid, but it will do so eventually.
X
X As a special case, alloca(0) reclaims storage without
X allocating any. It is a good idea to use alloca(0) in
X your main control loop, etc. to force garbage collection.
X*/
X#ifndef lint
Xstatic char SCCSid[] = "@(#)alloca.c 1.1"; /* for the "what" utility */
X#endif
X
X#ifdef HAVE_CONFIG_H
X#include "config.h"
X#endif
X
X#ifdef emacs
X#ifdef static
X/* actually, only want this if static is defined as ""
X -- this is for usg, in which emacs must undefine static
X in order to make unexec workable
X */
X#ifndef STACK_DIRECTION
Xyou
Xlose
X-- must know STACK_DIRECTION at compile-time
X#endif /* STACK_DIRECTION undefined */
X#endif /* static */
X#endif /* emacs */
X
X#ifndef alloca /* If compiling with GCC, this file's not needed. */
X
X#ifdef __STDC__
Xtypedef void *pointer; /* generic pointer type */
X#else
Xtypedef char *pointer; /* generic pointer type */
X#endif
X
X#define NULL 0 /* null pointer constant */
X
Xextern void free();
Xextern pointer xmalloc();
X
X/*
X Define STACK_DIRECTION if you know the direction of stack
X growth for your system; otherwise it will be automatically
X deduced at run-time.
X
X STACK_DIRECTION > 0 => grows toward higher addresses
X STACK_DIRECTION < 0 => grows toward lower addresses
X STACK_DIRECTION = 0 => direction of growth unknown
X*/
X
X#ifndef STACK_DIRECTION
X#define STACK_DIRECTION 0 /* direction unknown */
X#endif
X
X#if STACK_DIRECTION != 0
X
X#define STACK_DIR STACK_DIRECTION /* known at compile-time */
X
X#else /* STACK_DIRECTION == 0; need run-time code */
X
Xstatic int stack_dir; /* 1 or -1 once known */
X#define STACK_DIR stack_dir
X
Xstatic void
Xfind_stack_direction (/* void */)
X{
X static char *addr = NULL; /* address of first
X `dummy', once known */
X auto char dummy; /* to get stack address */
X
X if (addr == NULL)
X { /* initial entry */
X addr = &dummy;
X
X find_stack_direction (); /* recurse once */
X }
X else /* second entry */
X if (&dummy > addr)
X stack_dir = 1; /* stack grew upward */
X else
X stack_dir = -1; /* stack grew downward */
X}
X
X#endif /* STACK_DIRECTION == 0 */
X
X/*
X An "alloca header" is used to:
X (a) chain together all alloca()ed blocks;
X (b) keep track of stack depth.
X
X It is very important that sizeof(header) agree with malloc()
X alignment chunk size. The following default should work okay.
X*/
X
X#ifndef ALIGN_SIZE
X#define ALIGN_SIZE sizeof(double)
X#endif
X
Xtypedef union hdr
X{
X char align[ALIGN_SIZE]; /* to force sizeof(header) */
X struct
X {
X union hdr *next; /* for chaining headers */
X char *deep; /* for stack depth measure */
X } h;
X} header;
X
X/*
X alloca( size ) returns a pointer to at least `size' bytes of
X storage which will be automatically reclaimed upon exit from
X the procedure that called alloca(). Originally, this space
X was supposed to be taken from the current stack frame of the
X caller, but that method cannot be made to work for some
X implementations of C, for example under Gould's UTX/32.
X*/
X
Xstatic header *last_alloca_header = NULL; /* -> last alloca header */
X
Xpointer
Xalloca (size) /* returns pointer to storage */
X unsigned size; /* # bytes to allocate */
X{
X auto char probe; /* probes stack depth: */
X register char *depth = &probe;
X
X#if STACK_DIRECTION == 0
X if (STACK_DIR == 0) /* unknown growth direction */
X find_stack_direction ();
X#endif
X
X /* Reclaim garbage, defined as all alloca()ed storage that
X was allocated from deeper in the stack than currently. */
X
X {
X register header *hp; /* traverses linked list */
X
X for (hp = last_alloca_header; hp != NULL;)
X if ((STACK_DIR > 0 && hp->h.deep > depth)
X || (STACK_DIR < 0 && hp->h.deep < depth))
X {
X register header *np = hp->h.next;
X
X free ((pointer) hp); /* collect garbage */
X
X hp = np; /* -> next header */
X }
X else
X break; /* rest are not deeper */
X
X last_alloca_header = hp; /* -> last valid storage */
X }
X
X if (size == 0)
X return NULL; /* no allocation required */
X
X /* Allocate combined header + user data storage. */
X
X {
X register pointer new = xmalloc (sizeof (header) + size);
X /* address of header */
X
X ((header *)new)->h.next = last_alloca_header;
X ((header *)new)->h.deep = depth;
X
X last_alloca_header = (header *)new;
X
X /* User storage begins just after header. */
X
X return (pointer)((char *)new + sizeof(header));
X }
X}
X
X#endif /* no alloca */
END_OF_FILE
if test 5356 -ne `wc -c <'alloca.c'`; then
echo shar: \"'alloca.c'\" unpacked with wrong size!
fi
# end of 'alloca.c'
fi
if test -f 'README' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(1839 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XHey! Emacs! Yo! This is -*- Text -*- !!!
X
XThis GNU tar 1.11.2. Please send bug reports, etc., to
Xbug-gnu-utils@prep.ai.mit.edu. This is a beta-test release. Please
Xtry it out. There is no manual; the release of version 1.12 will
Xcontain a manual.
X
XGNU tar is based heavily on John Gilmore's public domain tar, but with
Xadded features. The manual is currently being written.
X
XThis distribution also includes rmt, the remote tape server (which
Xnormally must reside in /etc). The mt tape drive control program is
Xin the GNU cpio distribution.
X
XSee the file INSTALL for compilation and installation instructions for Unix.
XSee the file NEWS for information on all that is new in this version
Xof tar.
X
Xmakefile.pc is a makefile for Turbo C 2.0 on MS-DOS.
X
XVarious people have been having problems using floppies on a NeXT. In
Xorder to have them work right, you need to kill the automounting
Xprogram which tries to monut floppies as soon as they are added.
X
XIf you want to do incremental dumps, use the distributed backup
Xscripts. They are what we use at the FSF to do all our backups. Most
Ximportantly, do not use --incremental (-G) or --after-date (-N) or
X--newer-mtime to do incremental dumps. The only option that works
Xcorrectly for this purpose is --listed-incremental. (When extracting
Xincremental dumps, use --incremental (-G).)
X
XIf your system needs to link with -lPW to get alloca, but has
Xrename in the C library (so HAVE_RENAME is defined), -lPW might
Xgive you an incorrect version of rename. On HP-UX this manifests
Xitself as an undefined data symbol called "Error" when linking cp, ln,
Xand mv. If this happens, use `ar x' to extract alloca.o from libPW.a
Xand `ar rc' to put it in a library liballoca.a, and put that in LIBS
Xinstead of -lPW. This problem does not occur when using gcc, which
Xhas alloca built in.
X
END_OF_FILE
if test 1839 -ne `wc -c <'README'`; then
echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'INSTALL' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'INSTALL'\"
else
echo shar: Extracting \"'INSTALL'\" \(5733 characters\)
sed "s/^X//" >'INSTALL' <<'END_OF_FILE'
XThis is a generic INSTALL file for utilities distributions.
XIf this package does not come with, e.g., installable documentation or
Xdata files, please ignore the references to them below.
X
XTo compile this package:
X
X1. Configure the package for your system. In the directory that this
Xfile is in, type `./configure'. If you're using `csh' on an old
Xversion of System V, you might need to type `sh configure' instead to
Xprevent `csh' from trying to execute `configure' itself.
X
XThe `configure' shell script attempts to guess correct values for
Xvarious system-dependent variables used during compilation, and
Xcreates the Makefile(s) (one in each subdirectory of the source
Xdirectory). In some packages it creates a C header file containing
Xsystem-dependent definitions. It also creates a file `config.status'
Xthat you can run in the future to recreate the current configuration.
X
XRunning `configure' takes a minute or two. While it is running, it
Xprints some messages that tell what it is doing. If you don't want to
Xsee the messages, run `configure' with its standard output redirected
Xto `/dev/null'; for example, `./configure >/dev/null'.
X
XTo compile the package in a different directory from the one
Xcontaining the source code, you must use a version of `make' that
Xsupports the VPATH variable, such as GNU `make'. `cd' to the directory
Xwhere you want the object files and executables to go and run
X`configure'. `configure' automatically checks for the source code in
Xthe directory that `configure' is in and in `..'. If for some reason
X`configure' is not in the source code directory that you are
Xconfiguring, then it will report that it can't find the source code.
XIn that case, run `configure' with the option `--srcdir=DIR', where
XDIR is the directory that contains the source code.
X
XBy default, `make install' will install the package's files in
X/usr/local/bin, /usr/local/lib, /usr/local/man, etc. You can specify
Xan installation prefix other than /usr/local by giving `configure' the
Xoption `--prefix=PATH'. Alternately, you can do so by giving a value
Xfor the `prefix' variable when you run `make', e.g.,
X make prefix=/usr/gnu
X
XYou can specify separate installation prefixes for
Xarchitecture-specific files and architecture-independent files. If
Xyou give `configure' the option `--exec-prefix=PATH' or set the
X`make' variable `exec_prefix' to PATH, the package will use PATH as
Xthe prefix for installing programs and libraries. Data files and
Xdocumentation will still use the regular prefix. Normally, all files
Xare installed using the regular prefix.
X
XAnother `configure' option is useful mainly in `Makefile' rules for
Xupdating `config.status' and `Makefile'. The `--no-create' option
Xfigures out the configuration for your system and records it in
X`config.status', without actually configuring the package (creating
X`Makefile's and perhaps a configuration header file). Later, you can
Xrun `./config.status' to actually configure the package. You can also
Xgive `config.status' the `--recheck' option, which makes it re-run
X`configure' with the same arguments you used before. This option is
Xuseful if you change `configure'.
X
XSome packages pay attention to `--with-PACKAGE' options to `configure',
Xwhere PACKAGE is something like `gnu-libc' or `x' (for the X Window System).
XThe README should mention any --with- options that the package recognizes.
X
X`configure' ignores any other arguments that you give it.
X
XIf your system requires unusual options for compilation or linking
Xthat `configure' doesn't know about, you can give `configure' initial
Xvalues for some variables by setting them in the environment. In
XBourne-compatible shells, you can do that on the command line like
Xthis:
X CC='gcc -traditional' DEFS=-D_POSIX_SOURCE ./configure
X
XThe `make' variables that you might want to override with environment
Xvariables when running `configure' are:
X
X(For these variables, any value given in the environment overrides the
Xvalue that `configure' would choose:)
XCC C compiler program.
X Default is `cc', or `gcc' if `gcc' is in your PATH.
XINSTALL Program to use to install files.
X Default is `install' if you have it, `cp' otherwise.
X
X(For these variables, any value given in the environment is added to
Xthe value that `configure' chooses:)
XDEFS Configuration options, in the form `-Dfoo -Dbar ...'
X Do not use this variable in packages that create a
X configuration header file.
XLIBS Libraries to link with, in the form `-lfoo -lbar ...'
X
XIf you need to do unusual things to compile the package, we encourage
Xyou to figure out how `configure' could check whether to do them, and
Xmail diffs or instructions to the address given in the README so we
Xcan include them in the next release.
X
X2. Type `make' to compile the package. If you want, you can override
Xthe `make' variables CFLAGS and LDFLAGS like this:
X
X make CFLAGS=-O2 LDFLAGS=-s
X
X3. If the package comes with self-tests and you want to run them,
Xtype `make check'. If you're not sure whether there are any, try it;
Xif `make' responds with something like
X make: *** No way to make target `check'. Stop.
Xthen the package does not come with self-tests.
X
X4. Type `make install' to install programs, data files, and
Xdocumentation.
X
X5. You can remove the program binaries and object files from the
Xsource directory by typing `make clean'. To also remove the
XMakefile(s), the header file containing system-dependent definitions
X(if the package uses one), and `config.status' (all the files that
X`configure' created), type `make distclean'.
X
XThe file `configure.in' is used as a template to create `configure' by
Xa program called `autoconf'. You will only need it if you want to
Xregenerate `configure' using a newer version of `autoconf'.
END_OF_FILE
if test 5733 -ne `wc -c <'INSTALL'`; then
echo shar: \"'INSTALL'\" unpacked with wrong size!
fi
# end of 'INSTALL'
fi
if test -f 'NEWS' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'NEWS'\"
else
echo shar: Extracting \"'NEWS'\" \(3859 characters\)
sed "s/^X//" >'NEWS' <<'END_OF_FILE'
XCurrent Version: 1.11.2
X
X
XUser-visible changes since 1.11.1:
X
Xo Changes in backup scripts
X - cleaned up considerably; notices error conditions better over rsh
X - DUMP_REMIND_SCRIPT is now an option in backup-specs
X - new file dump-remind is an example of a DUMP_REMIND_SCRIPT
Xo Superfluous "Reading dirname" was a bug; fixed.
Xo Incompatibility problems with a bug on Solaris are fixed.
Xo New option --gzip (aliases are --ungzip and -z); calls gzip instead
X of compress. Also, --use-compress-program lets you specify any
X compress program. --compress-block is renamed --block-compress and
X now requires one of the three compression options to be specified.
Xo Several error messages are cleaned up.
Xo Directory owners are now set properly when running as root.
Xo Provide DUMP_REMIND_SCRIPT in backup-specs as a possible option
X for --info-script.
Xo Behave better with broken rmt servers.
Xo Dump scripts no longer use --atime-preserve; this causes a nasty probem.
Xo Several Makefile cleanups.
X
X
X==============
XVersion 1.11.1
XUser-visible changes since 1.11:
X
Xo Many bug fixes
X
X
X============
XVersion 1.11
XUser-visible changes since 1.10:
X
Xo Many bug fixes
X
Xo Now uses GNU standard configure, generated by Autoconf.
X
Xo Long options now use `--'; use of `+' is deprecated and support for it
X will eventually be removed.
X
Xo New option --null causes filenames read by -T to be null-terminated,
X and causes -C to be ignored.
X
Xo New option --remove-files deletes files (but not directories) after
X they are added to the archive.
X
Xo New option --ignore-failed-read prevents read-errors from affecting
X the exit status.
X
Xo New option --checkpoint prints occasional messages as the tape is
X being read or written.
X
Xo New option --show-omitted-dirs prints the names of directories
X omitted from the archive.
X
Xo Some tape drives which use a non-standard method of indicating
X end-of-tape now work correctly with multi-tape archives.
X
Xo --volno-file: Read the volume number used in prompting the user (but
X not in recording volume ID's on the archive) from a file.
X
Xo When using --multi-volume, you can now give multiple -f arguments;
X the various tape drives will get used in sequence and then wrap
X around to the beginning.
X
Xo Remote archive names no longer have to be in /dev: any file with a
X `:' is interpreted as remote. If new option --force-local is given,
X then even archive files with a `:' are considered local.
X
Xo New option --atime-preserve restores (if possible) atimes to their
X original values after dumping the file.
X
Xo No longer does tar confusingly dump "." when you don't tell it what
X to dump.
X
Xo When extracting directories, tar now correctly restores their
X modification and access times.
X
Xo Longnames support is redone differently--long name info directly
X precedes the long-named file or link in the archive, so you no
X longer have to wait for the extract to hit the end of the tape for
X long names to work.
X
X
X=============
XVersion 1.10:
XUser-visible changes since 1.09:
X
XFilename to -G is optional. -C works right.
XNames +newer and +newer-mtime work right.
X
X-g is now +incremental
X-G is now +listed-incremental
X
XSparse files now work correctly.
X
X+volume is now called +label.
X
X+exclude now takes a filename argument, and +exclude-from does what
X+exclude used to do.
X
XExit status is now correct.
X
X+totals keeps track of total I/O and prints it when tar exits.
X
XWhen using +label with +extract, the label is now a regexp.
X
XNew option +tape-length (-L) does multi-volume handling like BSD dump:
Xyou tell tar how big the tape is and it will prompt at that point
Xinstead of waiting for a write error.
X
XNew backup scripts level-0 and level-1 which might be useful to
Xpeople. They use a file "backup-specs" for information, and shouldn't
Xneed local modification. These are what we use to do all our backups
Xat the FSF.
END_OF_FILE
if test 3859 -ne `wc -c <'NEWS'`; then
echo shar: \"'NEWS'\" unpacked with wrong size!
fi
# end of 'NEWS'
fi
if test -f 'COPYING' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'COPYING'\"
else
echo shar: Extracting \"'COPYING'\" \(17982 characters\)
sed "s/^X//" >'COPYING' <<'END_OF_FILE'
X GNU GENERAL PUBLIC LICENSE
X Version 2, June 1991
X
X Copyright (C) 1989, 1991 Free Software Foundation, Inc.
X 675 Mass Ave, Cambridge, MA 02139, USA
X Everyone is permitted to copy and distribute verbatim copies
X of this license document, but changing it is not allowed.
X
X Preamble
X
X The licenses for most software are designed to take away your
Xfreedom to share and change it. By contrast, the GNU General Public
XLicense is intended to guarantee your freedom to share and change free
Xsoftware--to make sure the software is free for all its users. This
XGeneral Public License applies to most of the Free Software
XFoundation's software and to any other program whose authors commit to
Xusing it. (Some other Free Software Foundation software is covered by
Xthe GNU Library General Public License instead.) You can apply it to
Xyour programs, too.
X
X When we speak of free software, we are referring to freedom, not
Xprice. Our General Public Licenses are designed to make sure that you
Xhave the freedom to distribute copies of free software (and charge for
Xthis service if you wish), that you receive source code or can get it
Xif you want it, that you can change the software or use pieces of it
Xin new free programs; and that you know you can do these things.
X
X To protect your rights, we need to make restrictions that forbid
Xanyone to deny you these rights or to ask you to surrender the rights.
XThese restrictions translate to certain responsibilities for you if you
Xdistribute copies of the software, or if you modify it.
X
X For example, if you distribute copies of such a program, whether
Xgratis or for a fee, you must give the recipients all the rights that
Xyou have. You must make sure that they, too, receive or can get the
Xsource code. And you must show them these terms so they know their
Xrights.
X
X We protect your rights with two steps: (1) copyright the software, and
X(2) offer you this license which gives you legal permission to copy,
Xdistribute and/or modify the software.
X
X Also, for each author's protection and ours, we want to make certain
Xthat everyone understands that there is no warranty for this free
Xsoftware. If the software is modified by someone else and passed on, we
Xwant its recipients to know that what they have is not the original, so
Xthat any problems introduced by others will not reflect on the original
Xauthors' reputations.
X
X Finally, any free program is threatened constantly by software
Xpatents. We wish to avoid the danger that redistributors of a free
Xprogram will individually obtain patent licenses, in effect making the
Xprogram proprietary. To prevent this, we have made it clear that any
Xpatent must be licensed for everyone's free use or not licensed at all.
X
X The precise terms and conditions for copying, distribution and
Xmodification follow.
X
X GNU GENERAL PUBLIC LICENSE
X TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
X
X 0. This License applies to any program or other work which contains
Xa notice placed by the copyright holder saying it may be distributed
Xunder the terms of this General Public License. The "Program", below,
Xrefers to any such program or work, and a "work based on the Program"
Xmeans either the Program or any derivative work under copyright law:
Xthat is to say, a work containing the Program or a portion of it,
Xeither verbatim or with modifications and/or translated into another
Xlanguage. (Hereinafter, translation is included without limitation in
Xthe term "modification".) Each licensee is addressed as "you".
X
XActivities other than copying, distribution and modification are not
Xcovered by this License; they are outside its scope. The act of
Xrunning the Program is not restricted, and the output from the Program
Xis covered only if its contents constitute a work based on the
XProgram (independent of having been made by running the Program).
XWhether that is true depends on what the Program does.
X
X 1. You may copy and distribute verbatim copies of the Program's
Xsource code as you receive it, in any medium, provided that you
Xconspicuously and appropriately publish on each copy an appropriate
Xcopyright notice and disclaimer of warranty; keep intact all the
Xnotices that refer to this License and to the absence of any warranty;
Xand give any other recipients of the Program a copy of this License
Xalong with the Program.
X
XYou may charge a fee for the physical act of transferring a copy, and
Xyou may at your option offer warranty protection in exchange for a fee.
X
X 2. You may modify your copy or copies of the Program or any portion
Xof it, thus forming a work based on the Program, and copy and
Xdistribute such modifications or work under the terms of Section 1
Xabove, provided that you also meet all of these conditions:
X
X a) You must cause the modified files to carry prominent notices
X stating that you changed the files and the date of any change.
X
X b) You must cause any work that you distribute or publish, that in
X whole or in part contains or is derived from the Program or any
X part thereof, to be licensed as a whole at no charge to all third
X parties under the terms of this License.
X
X c) If the modified program normally reads commands interactively
X when run, you must cause it, when started running for such
X interactive use in the most ordinary way, to print or display an
X announcement including an appropriate copyright notice and a
X notice that there is no warranty (or else, saying that you provide
X a warranty) and that users may redistribute the program under
X these conditions, and telling the user how to view a copy of this
X License. (Exception: if the Program itself is interactive but
X does not normally print such an announcement, your work based on
X the Program is not required to print an announcement.)
X
XThese requirements apply to the modified work as a whole. If
Xidentifiable sections of that work are not derived from the Program,
Xand can be reasonably considered independent and separate works in
Xthemselves, then this License, and its terms, do not apply to those
Xsections when you distribute them as separate works. But when you
Xdistribute the same sections as part of a whole which is a work based
Xon the Program, the distribution of the whole must be on the terms of
Xthis License, whose permissions for other licensees extend to the
Xentire whole, and thus to each and every part regardless of who wrote it.
X
XThus, it is not the intent of this section to claim rights or contest
Xyour rights to work written entirely by you; rather, the intent is to
Xexercise the right to control the distribution of derivative or
Xcollective works based on the Program.
X
XIn addition, mere aggregation of another work not based on the Program
Xwith the Program (or with a work based on the Program) on a volume of
Xa storage or distribution medium does not bring the other work under
Xthe scope of this License.
X
X 3. You may copy and distribute the Program (or a work based on it,
Xunder Section 2) in object code or executable form under the terms of
XSections 1 and 2 above provided that you also do one of the following:
X
X a) Accompany it with the complete corresponding machine-readable
X source code, which must be distributed under the terms of Sections
X 1 and 2 above on a medium customarily used for software interchange; or,
X
X b) Accompany it with a written offer, valid for at least three
X years, to give any third party, for a charge no more than your
X cost of physically performing source distribution, a complete
X machine-readable copy of the corresponding source code, to be
X distributed under the terms of Sections 1 and 2 above on a medium
X customarily used for software interchange; or,
X
X c) Accompany it with the information you received as to the offer
X to distribute corresponding source code. (This alternative is
X allowed only for noncommercial distribution and only if you
X received the program in object code or executable form with such
X an offer, in accord with Subsection b above.)
X
XThe source code for a work means the preferred form of the work for
Xmaking modifications to it. For an executable work, complete source
Xcode means all the source code for all modules it contains, plus any
Xassociated interface definition files, plus the scripts used to
Xcontrol compilation and installation of the executable. However, as a
Xspecial exception, the source code distributed need not include
Xanything that is normally distributed (in either source or binary
Xform) with the major components (compiler, kernel, and so on) of the
Xoperating system on which the executable runs, unless that component
Xitself accompanies the executable.
X
XIf distribution of executable or object code is made by offering
Xaccess to copy from a designated place, then offering equivalent
Xaccess to copy the source code from the same place counts as
Xdistribution of the source code, even though third parties are not
Xcompelled to copy the source along with the object code.
X
X 4. You may not copy, modify, sublicense, or distribute the Program
Xexcept as expressly provided under this License. Any attempt
Xotherwise to copy, modify, sublicense or distribute the Program is
Xvoid, and will automatically terminate your rights under this License.
XHowever, parties who have received copies, or rights, from you under
Xthis License will not have their licenses terminated so long as such
Xparties remain in full compliance.
X
X 5. You are not required to accept this License, since you have not
Xsigned it. However, nothing else grants you permission to modify or
Xdistribute the Program or its derivative works. These actions are
Xprohibited by law if you do not accept this License. Therefore, by
Xmodifying or distributing the Program (or any work based on the
XProgram), you indicate your acceptance of this License to do so, and
Xall its terms and conditions for copying, distributing or modifying
Xthe Program or works based on it.
X
X 6. Each time you redistribute the Program (or any work based on the
XProgram), the recipient automatically receives a license from the
Xoriginal licensor to copy, distribute or modify the Program subject to
Xthese terms and conditions. You may not impose any further
Xrestrictions on the recipients' exercise of the rights granted herein.
XYou are not responsible for enforcing compliance by third parties to
Xthis License.
X
X 7. If, as a consequence of a court judgment or allegation of patent
Xinfringement or for any other reason (not limited to patent issues),
Xconditions are imposed on you (whether by court order, agreement or
Xotherwise) that contradict the conditions of this License, they do not
Xexcuse you from the conditions of this License. If you cannot
Xdistribute so as to satisfy simultaneously your obligations under this
XLicense and any other pertinent obligations, then as a consequence you
Xmay not distribute the Program at all. For example, if a patent
Xlicense would not permit royalty-free redistribution of the Program by
Xall those who receive copies directly or indirectly through you, then
Xthe only way you could satisfy both it and this License would be to
Xrefrain entirely from distribution of the Program.
X
XIf any portion of this section is held invalid or unenforceable under
Xany particular circumstance, the balance of the section is intended to
Xapply and the section as a whole is intended to apply in other
Xcircumstances.
X
XIt is not the purpose of this section to induce you to infringe any
Xpatents or other property right claims or to contest validity of any
Xsuch claims; this section has the sole purpose of protecting the
Xintegrity of the free software distribution system, which is
Ximplemented by public license practices. Many people have made
Xgenerous contributions to the wide range of software distributed
Xthrough that system in reliance on consistent application of that
Xsystem; it is up to the author/donor to decide if he or she is willing
Xto distribute software through any other system and a licensee cannot
Ximpose that choice.
X
XThis section is intended to make thoroughly clear what is believed to
Xbe a consequence of the rest of this License.
X
X 8. If the distribution and/or use of the Program is restricted in
Xcertain countries either by patents or by copyrighted interfaces, the
Xoriginal copyright holder who places the Program under this License
Xmay add an explicit geographical distribution limitation excluding
Xthose countries, so that distribution is permitted only in or among
Xcountries not thus excluded. In such case, this License incorporates
Xthe limitation as if written in the body of this License.
X
X 9. The Free Software Foundation may publish revised and/or new versions
Xof the General Public License from time to time. Such new versions will
Xbe similar in spirit to the present version, but may differ in detail to
Xaddress new problems or concerns.
X
XEach version is given a distinguishing version number. If the Program
Xspecifies a version number of this License which applies to it and "any
Xlater version", you have the option of following the terms and conditions
Xeither of that version or of any later version published by the Free
XSoftware Foundation. If the Program does not specify a version number of
Xthis License, you may choose any version ever published by the Free Software
XFoundation.
X
X 10. If you wish to incorporate parts of the Program into other free
Xprograms whose distribution conditions are different, write to the author
Xto ask for permission. For software which is copyrighted by the Free
XSoftware Foundation, write to the Free Software Foundation; we sometimes
Xmake exceptions for this. Our decision will be guided by the two goals
Xof preserving the free status of all derivatives of our free software and
Xof promoting the sharing and reuse of software generally.
X
X NO WARRANTY
X
X 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
XFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
XOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
XPROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
XOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
XMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
XTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
XPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
XREPAIR OR CORRECTION.
X
X 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
XWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
XREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
XINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
XOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
XTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
XYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
XPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
XPOSSIBILITY OF SUCH DAMAGES.
X
X END OF TERMS AND CONDITIONS
X
X Appendix: How to Apply These Terms to Your New Programs
X
X If you develop a new program, and you want it to be of the greatest
Xpossible use to the public, the best way to achieve this is to make it
Xfree software which everyone can redistribute and change under these terms.
X
X To do so, attach the following notices to the program. It is safest
Xto attach them to the start of each source file to most effectively
Xconvey the exclusion of warranty; and each file should have at least
Xthe "copyright" line and a pointer to where the full notice is found.
X
X <one line to give the program's name and a brief idea of what it does.>
X Copyright (C) 19yy <name of author>
X
X This program is free software; you can redistribute it and/or modify
X it under the terms of the GNU General Public License as published by
X the Free Software Foundation; either version 2 of the License, or
X (at your option) any later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
XAlso add information on how to contact you by electronic and paper mail.
X
XIf the program is interactive, make it output a short notice like this
Xwhen it starts in an interactive mode:
X
X Gnomovision version 69, Copyright (C) 19yy name of author
X Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
X This is free software, and you are welcome to redistribute it
X under certain conditions; type `show c' for details.
X
XThe hypothetical commands `show w' and `show c' should show the appropriate
Xparts of the General Public License. Of course, the commands you use may
Xbe called something other than `show w' and `show c'; they could even be
Xmouse-clicks or menu items--whatever suits your program.
X
XYou should also get your employer (if you work as a programmer) or your
Xschool, if any, to sign a "copyright disclaimer" for the program, if
Xnecessary. Here is a sample; alter the names:
X
X Yoyodyne, Inc., hereby disclaims all copyright interest in the program
X `Gnomovision' (which makes passes at compilers) written by James Hacker.
X
X <signature of Ty Coon>, 1 April 1989
X Ty Coon, President of Vice
X
XThis General Public License does not permit incorporating your program into
Xproprietary programs. If your program is a subroutine library, you may
Xconsider it more useful to permit linking proprietary applications with the
Xlibrary. If this is what you want to do, use the GNU Library General
XPublic License instead of this License.
END_OF_FILE
if test 17982 -ne `wc -c <'COPYING'`; then
echo shar: \"'COPYING'\" unpacked with wrong size!
fi
# end of 'COPYING'
fi
if test -f 'ChangeLog' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'ChangeLog'\"
else
echo shar: Extracting \"'ChangeLog'\" \(61978 characters\)
sed "s/^X//" >'ChangeLog' <<'END_OF_FILE'
XThu Mar 25 13:32:40 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * version.c: Released version 1.11.2.
X
X * Makefile.in (dist): Do the link differently; some of the
X files have changed filesystems which makes it more complex.
X
X * Makefile.in (dist, shar): Use gzip instead of compress.
X
X * create.c (dump_file): Test for curdev==-1, not curdev<0.
X Some losing NFS systems give negative device numbers
X sometimes.
X
XThu Mar 25 11:55:15 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
X
X * level-0, level-1 (TAR_PART1): Use `--block-size', not just
X `--block', which is now ambiguous.
X
XWed Mar 24 22:12:51 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
X
X * backup-specs (TAR): New variable.
X
X * level-0, level-1 (TAR_PART1): Get path of GNU tar from `TAR'
X variable, don't hardcode it.
X
XSat Mar 20 00:20:05 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
X
X * backup-specs (SLEEP_MESSAGE): put backslashes in front of nested
X double quotes.
X
X * level-0, level-1 (BACKUP_DIRS): Don't put in quotes.
X (LOGFILE): Use sed to construct name, not awk.
X
X * dump-remind (recipients): Replaced inefficient pipeline with a
X single, simple sed script.
X (volno): Deal with the possibility that VOLNO_FILE may not be
X created yet.
X
XFri Mar 19 15:05:15 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * backup-specs (VOLNO_FILE): Removed abusive comment by Noah.
X
X * buffer.c (new_volume): Write the global volume number to the
X volno file before running the info script, so that the script
X can look at it.
X
XThu Mar 18 20:11:54 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
X
X * Makefile.in (AUX): Include `dump-remind' in distribution.
X
X * backup-specs (SLEEP_MESSAGE): New variable.
X level-0, level-1: Use it instead of external `dont_touch' file.
X
X * level-0, level-1: Put most of the script in () and pipe
X everything from the subshell through tee -a $LOGFILE. Since you
X really want most of the output to go to the logfile anyway, and
X since all those pipelines were preventing one from getting the
X exit status of most commands, this seems like the right idea.
X
X * level-0, level-1 (LOGFILE): Use YYYY-MM-DD (all numeric) format
X for log file name, since that makes the file names sortable in a
X coherent way. Suffix should always be `level-n' where n is the
X dump level. level-0 script was just using `-full' instead.
X
X * level-0, level-1 (DUMP_LEVEL): New variable. Set to `0' or `1'
X in each script as appropriate.
X
X * level-0, level-1 (HOST): Renamed to `localhost' for clarity.
X (host): renamed to `remotehost' for clarity.
X
X * level-0, level-1 (startdate): New variable. Use it in Subject
X line of mailed report.
X
X * level-0, level-1: Fixed all instances where sed is called with a
X script on the command line to use `-e' option.
X
X * level-0, level-1: Don't try to call logfile.sed to filter
X LOGFILE. It's not distributed with tar and was never really used
X anyway.
X
X * level-0, level-1: Put quotes around most variable names (barring
X those that are known to intentionally contain text that should be
X expanded into multiple words, like `TAR_PART1').
X
X * level-0, level-1: Got rid of annoying trailing backslashes in awk
X scripts. They were gratuitous. Made them a little more readable
X by adding some whitespace.
X
XWed Mar 17 10:30:58 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * tar.c (describe, long_options): Changed --compress-block to
X --block-compress.
X (options): Fixed f_compress_block sanity check error message
X to give the correct name of the option.
X
XTue Mar 16 14:52:40 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * extract.c (extract_archive): case LF_DIR: Do chown when
X necessary. Don't bother jumping to set_filestat for
X f_modified; repeat the chmod code here. Replace `break',
X deleted on 2 September 1992.
X
X * tar.c (describe, long_options, options): Added gzip options
X and use-compress-program option.
X * tar.h: Added new compression options.
X * buffer.c (child_open, open_archive): Use new compression options.
X
X * create.c (start_header): Only mask off high bits when
X creating old-style archives.
X * list.c (decode_header): Mask off potentially misleading
X high bits from the mode when reading headers.
X
XMon Mar 15 11:34:34 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * extract.c (extract_archive): Put arguments in the right
X order for error message.
X
X * create.c (deal_with_sparse): if the last byte was null, we
X didn't write it out.
X
X * gnu.c, create.c, extract.c, diffarch.c, list.c throughout:
X Replace malloc calls with ck_malloc and realloc with ck_realloc.
X
X * tar.c (describe): Improve doc for -L.
X
X * tar.c (name_next): Don't apply exclusion to explicitly named
X files.
X
X * tar.c (long_options, describe): Added new-volume-script as
X an alias for info-script.
X
X * extract.c (extract_archive): LF_DUMPDIR case; misplaced paren.
X
X * extract.c (extract_archive): extract_file case, first if,
X include space for null in namelen computation.
X
X * extract.c (extract_sparse_file): Use value returned by write
X to properly create error message.
X
X * create.c (create_archive): Don't assume we have anything to
X dump.
X
X * buffer.c (open_archive): Set current_file_name for the
X volume header so that verbose listings work properly.
X
X * Makefile.in (realclean): Added getdate.c.
X
XThu Jan 14 23:38:44 1993 David J. MacKenzie (djm@kropotkin.gnu.ai.mit.edu)
X
X * tar.c: Include fnmatch.h after port.h to make sure we get our FNM_*
X (e.g. on HPUX 8).
X
XTue Nov 24 08:30:54 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu)
X
X * tar.c (addname), gnu.c (read_dir_file): Use HAVE_GETCWD, not USG.
X
X * port.h, rmt.h: Use HAVE_STRING_H, not USG.
X
X * port.h: Add dir header decls.
X * create.c, gnu.c: Use SYSNDIR, SYSDIR, and NDIR
X instead of BSD42 and USG. Rename DP_NAMELEN to NLENGTH.
X Use `struct dirent' instead of `struct direct'.
X * create.c, gnu.c, tar.c: Remove dir header decls.
X
XWed Nov 18 15:31:30 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu)
X
X * tar.c: Change FNM_TARPATH to FNM_LEADING_DIR to match change
X in fnmatch.[ch].
X
XWed Oct 21 00:52:24 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
X
X * level-0, level-1: put curly braces around variables for clarity.
X
X * backup-specs (DUMP_REMIND_SCRIPT): define it (but commented out
X so that distributed dump scripts won't use it by default).
X level-0, level-1 (TAR_PART1): use --info-script if
X DUMP_REMIND_SCRIPT is defined.
X dump-remind: new file (intended as an example).
X
XThu Oct 15 03:33:28 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
X
X * level-0, level-1: remove $LOGFILE.tmp files before exiting.
X
XFri Oct 2 00:28:01 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu)
X
X * tar.c (describe): Fix some tab alignments.
X
X * Makefile.in (SRC3): Add getdate.c, for systems without bison/yacc
X (like MS-DOS).
X
X * diffarch.c (diff_sparse_files): Add missing arg to fprintf calls.
X
X * extract.c (extract_archive, restore_saved_dir_info),
X buffer.c (child_open), list.c (decode_header, print_header):
X Delete unused vars.
X
X * port.c [__MSDOS__]: Have strstr, rename, and mkdir. Don't
X define ck_pipe.
X
X * buffer.c, tar.c (init_volume_number, closeout_volume_number),
X create.c (write_long): Declare as void, not int, since they
X don't return a value.
X
XThu Sep 24 00:06:02 1992 Michael I Bushnell (mib@churchy.gnu.ai.mit.edu)
X
X * level-0, level-1 (TAR_PART1): remove --atime-preserve
X because of a total screw.
X
XTue Sep 22 14:15:48 1992 Michael I Bushnell (mib@wookumz.gnu.ai.mit.edu)
X
X * buffer.c (close_archive): Removed leftover `break' from when
X this was a switch.
X
XTue Sep 22 08:33:16 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
X
X * create.c, port.h: indented #pragma directives with 1 space.
X
XFri Sep 18 14:15:17 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * All source files: re indented using GNU indent.
X
X * rtapelib.c (__rmt_read): Only read the amount left in the
X buffer; otherwise a broken rmt server (which puts too much
X data out) could overwrite past our buffer.
X
XThu Sep 17 14:08:58 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * create.c: Throughout, use struct utimbuf rather than array
X of longs.
X
X * configure.in: Check for getpwuid and getgrgid.
X
X * Makefile.in (SRC3, AUX): Move alloca.c to SRC3.
X (OBJ3): Add @ALLOCA@.
X
X * Makefile.in (getdate.c): Look in srcdir for getdate.y.
X
X * buffer.c (close_archive): We can't check WTERMSIG
X meaningfully unless we already know tha WIFSIGNALED is true.
X (There is no guarantee it WTERMSIG will return zero when
X WIFSIGNALED is false.)
X * port.c (rmdir, mkdir): Check WIFSIGNALED rather than
X WTERMSIG.
X
X * Makefile.in (getdate.c): Use $(YACC) instead of `yacc'.
X
XTue Sep 15 14:49:48 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * version.c: Released version 1.11.1.
X
X * Makefile (AUX): Added NEWS.
X
X * Makefile.in (rmt): Added $(LIBS).
X * configure.in: Added tests for libraries needed on Solaris.
X
X * mangle.c (extract_mangle): Null terminate link name for
X losing archives missing it.
X
X * Makefile.in: added target and rule for getdate.c: getdate.y;
X some makes don't have one built in.
X
XMon Sep 14 16:23:15 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * tar.c (options, main): Advise use of --help rather than
X +help.
X
X * create.c (write_long): Using hstat here is a Bad Idea, and
X totally unnecessary at that.
X
X * list.c (read_header): Compute both signed and normal
X checksums.
X
X * configure.in: Define BSD in the presence of /sdmach or
X /../../mach.
X
X * diffarch.c, buffer.c: Declare valloc as void* rather than
X char*.
X
X * Makefile.in: Don't install info files.
X
X * configure.in: Check for malloc was scrambled.
X
X * port.h: Undefine index and rindex if necessary; some
X string.h's define them for us.
X
X * tar.c (addname): Missing braces after if.
X * gnu.c (read_dir_file): Missing braces after if.
X
X * names.c: Add include of <stdio.h>,
X
X * create.c (start_header): Set current_file_name so that
X print_header (used for verbose create) works properly.
X (dump_file): Set current_link_name when setting up symlink
X and hardlink records.
X
XFri Sep 11 01:05:52 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
X
X * fnmatch.[ch]: New files.
X * wildmat.c: File removed.
X * tar.c: Include fnmatch.h and use fnmatch instead of wildmat.
X * Makefile.in, makefile.pc: Replace wildmat.o(bj) with fnmatch.
X
XThu Sep 10 23:19:30 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
X
X * buffer.c, tar.c: Remove redundant decls of getenv, rindex.
X
X * Makefile.in: Add uninstall target.
X Define libdir instead of hardcoding /etc for installing rmt.
X
XThu Sep 10 13:06:03 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * list.c (read_header): On second thought, that doesn't work
X either, so just store the names in malloced areas. Sigh.
X
X * NEWS: New file.
X * README: Removed things that belong in NEWS; point to it.
X
X * list.c (read_header): current_file_name and
X current_link_name need to be set to the arrays in head rather
X than header; header is the actual read buffer and will change.
X
X * extract.c (extract_archive):
X * buffer.c (new_volume): `#' directives need to start in
X column 1.
X
XThu Sep 10 06:09:18 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
X
X * level-0, level-1 (TAR_PART1): put --atime-preserve inside quotes.
X
XWed Sep 9 13:34:26 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * Makefile.in (AUX): Add getpagesize.h.
X (AUX): Comment out manuals.
X (all): Comment out dependency on tar.info.
X
X * version.c: Release of version 1.11.
X
X * level-0, level-1 (TAR_PART1): Use --atime-preserve.
X
X * Makefile, configure.in: Arrange to use local malloc on HP-UX.
X
X * port.h Use the canonical Autoconf chunk for alloca instead
X of just looking for gcc.
X
XWed Sep 9 03:16:58 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
X
X * port.h: If compiling with gcc, use __builtin_alloca.
X
XTue Sep 8 16:13:41 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * extract.c: Removed long name support from here.
X * list.c (read_header): Understand and skip longname/longlink
X headers here. Names for current file are stored in new global
X variables. All source files except create.c changed to refer
X to current_file_name and current_link_name instead of fields
X directly from the current header.
X
XThu Sep 3 12:41:08 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * create.c (write_long): New function.
X (dump_file): When writing link records or symlink records, use
X new write_long function instead of mangling when the link
X target is too long.
X (start_header): Use write_long instead of mangling for long
X names.
X * extract.c (saverec): Recognize LF_LONGNAME and LF_LONGLINK.
X (saverec): Throughout, use longname and longlink if they are set.
X
XWed Sep 2 14:41:13 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * mangle.c: This is now deprecated; retain extract_mangle for
X backward compatability.
X
X * list.c (print_header): patch from Chris Arthur to prevent
X printing 0 when the gid or uid is null.
X
X * list.c (decode_header): patch from Chris Arthur to use the
X gid field when the gid is empty, and similarly for uid.
X
X * extract.c: saved_dir_info, saved_dir_info_head: new type and
X var.
X (extract_archive): When extracting directories, now save info
X in saved_dir_info_head.
X (restore_saved_dir_info): New function.
X * list.c (read_and): Call restore_saved_dir_info at the end of
X the run.
X This patch is from Chris Arthur (csa@pennies.sw.stratus.com).
X
XMon Aug 31 15:39:55 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * create.c (create_archive): If there are no names specified,
X write nothing on the archive instead of dumping ".".
X
X * buffer.c (open_archive): Useful error message.
X
X * tar.c, tar.h: Recognize f_atime_preserve.
X * create.c (dump_file): Implement f_atime_preserve.
X
X * rmt.h (_remdev): Don't require /dev/ to be in remote archive
X names; obey new force-local flag.
X * tar.c, tar.h: Implement new force-local flag.
X
X * tar.c (describe): same-owner and same-order were confused.
X
X * create.c (dump_file): Check for toplevel had sense reversed.
X
X * buffer.c (new_archive): Don't free old_name...when these
X come from the command line, they aren't malloced, and it isn't
X important to save this trivial amount of memory.
X
X * tar.h: replace ar_file with ar_files, n_ar_files,
X cur_ar_files.
X * buffer.c (open_archive): multi-volume compressed archives
X never worked; give an appropriate error. Change open of
X ar_file to open of ar_files[0].
X (writeerror, readerror, flush_archive): use
X ar_files[cur_ar_file] instead of ar_file.
X (new_archive): Necessary changes to support ar_files.
X * tar.c (options): handle multiple tape drive arguments.
X
XFri Aug 28 17:42:13 1992 Michael I Bushnell (mib@wookumz.gnu.ai.mit.edu)
X
X * list.c (decode_header), create.c (start_header), tar.h (TMAGIC):
X Undo djm's changes below; tar does not support the final
X Posix.1 format; it's bad to make it look like it does.
X
XSun Jul 19 02:13:46 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
X
X * port.h: Try to prevent redefining major.
X * port.c: HAVE_BZERO -> minix. Fix a typo.
X
X * list.c (decode_header): Recognize the final POSIX.1 magic as
X well as the early draft magic for ustar.
X * create.c (start_header): Create a final POSIX.1 magic string
X instead of an early draft string for ustar.
X * tar.h (TMAGIC): Remove the trailing blanks.
X
X * rmt.c, rtapelib.c: Use POSIX and STDC headers if available.
X * rmt.h: Declare the external functions defined in rtapelib.c.
X
XTue Jul 14 00:44:37 1992 David J. MacKenzie (djm@apple-gunkies.gnu.ai.mit.edu)
X
X * pathmax.h: New file.
X * port.h: Include it.
X * create.c (create_archive): Allocate PATH_MAX instead of
X NAME_MAX for temporary buffer so we don't have to figure out
X what NAME_MAX is (portably).
X
XFri Jul 10 08:30:42 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * gnu.c (collect_and_sort_names): write_dir_file has no argument.
X
X * level-0, level-1: Avoid silly Sun awk lossage.
X
XMon Jul 6 20:11:32 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
X
X * port.c (rename): If unlinking the source at the end fails,
X unlink the destination instead to avoid leaving a mess.
X
XFri Jul 3 15:16:42 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
X
X * buffer.c, diffarch.c, update.c, rtapelib.c: Change NO_MTIO to
X HAVE_SYS_MTIO_H.
X
X * port.c, tar.h: Change FOO_MISSING to HAVE_FOO.
X
XTue Jun 23 23:39:02 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu)
X
X * rmt.c: Add #ifdefs to work on ISC.
X
XWed May 20 00:12:27 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
X
X * port.h: Define major, minor, makedev if the system doesn't.
X
XWed May 13 21:16:38 1992 Michael I Bushnell (mib@apple-gunkies.gnu.ai.mit.edu)
X
X * gnu.c (add_dir_name): Store legitimate value into
X dir_contents when get_dir_contents returns NULL.
X
XThu May 7 23:44:35 1992 Michael I Bushnell (mib@apple-gunkies.gnu.ai.mit.edu)
X
X * gnu.c (add_dir_name): Check for return of NULL from get_dir_contents;
X see djm's change of Fri Jul 26 01:12:58 1991.
X
XMon May 4 22:50:57 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
X
X * tar.h: Make comments for option names say -- instead of +.
X
XThu Apr 30 03:09:16 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
X
X * level-1: Added `$' before VOLNO_FILE in definition of TAR_PART1.
X Added line to remove $VOLNO_FILE from any previous dump before
X starting.
X
X * level-0, level-1: Change long options to use `--' instead of `+'
X (support for `+' will go away soon)
X
XWed Apr 29 14:23:10 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * tar.c, tar.t: Added +volno-file option.
X buffer.c: New functions init_volume_number,
X closeout_volume_number.
X tar.c (main): Call new functions in the right place.
X
X * buffer.c (fl_write, fl_read): Mod to allow losing tape
X drives which use short counts to indicate end of tape
X correctly handle the multi-tape stuff. The read half won't
X co-exist with f_reblock; there's no way to fix that, of
X course.
X
X * tar.c, tar.h: Added new option +show-omitted-dirs, from
X Karl Berry.
X list.c (read_and): Implemented show-omitted-dirs.
X
X * tar.c, tar.h: Added new option +checkpoint.
X buffer.c (fl_read, fl_write): Implemented +checkpoint lazily.
X
X * create.c (dump_file): Added toplevel argument; some devices
X can be negative, so the old method was bogus. All callers
X changed.
X
X * tar.c, tar.h: Added new option +ignore-failed-read.
X create.c (dump_file): Implemented +ignore-failed-read.
X
X * create.c (finish_sparse_file): Commented out debugging printf.
X
X * tar.c, tar.h: Added new option +remove-files to delete files
X after they are added to the archive.
X create.c (dump_file): Implemented +remove-files for
X everything but directories. I don't think they need it.
X
XTue Apr 28 13:21:42 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * create.c: (dump_file): save_name needs to be set equal to p,
X not something inside the header, because the header changes at
X the first buffer flush.
X
XFri Apr 24 10:41:13 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * create.c: Djm incorrectly moved the include of port.h to
X precede the include of sys/file.h; restored.
X
X * tar.c (main): Cases CMD_EXTRACT and CMD_LIST: declare error
X string with const.
X
X * gnu.c (collect_and_sort_names): Leave if around
X write_dir_file in place.
X
XWed Apr 22 02:16:14 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
X
X * rtapelib.c: SIGTYPE -> RETSIGTYPE.
X
XMon Mar 9 22:42:05 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
X
X * rtapelib.c: Reformat and make comments more complete.
X Rename a few variables for clarity.
X
XThu Mar 5 14:07:34 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
X
X * tar.c (describe): Document long options as starting with --.
X
XThu Jan 23 22:54:41 1992 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
X
X * tar.c (options): Check get_date return value for error indication.
X
XTue Dec 24 00:03:03 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
X
X * tar.c, gnu.c, extract.c, create.c, port.h, rmt.h: Change
X POSIX ifdefs to HAVE_UNISTD_H and _POSIX_VERSION.
X
XFri Dec 20 13:50:38 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
X
X * testpad.c (main): flush stderr so perror and fprintf
X cooperate right.
X
XWed Dec 18 16:52:42 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
X
X * port.h: Check MAJOR_IN_MKDEV and MAJOR_IN_SYSMACROS to find
X where to get major, minor and makedev.
X * create.c, list.c, update.c: Don't check USG to include
X sys/sysmacros.h.
X
XThu Dec 12 21:57:10 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
X
X * mangle.c (extract_mangle): Correctly null terminate name of
X link target.
X
XThu Nov 21 07:44:18 1991 Michael I Bushnell (mib at nutrimat)
X
X * create.c (dump_file, at start of ISREG output loop): use
X filename from header instead of real name to make sure that we
X get the mangled version and not one that is too long and
X overflows buffers.
X
XSat Nov 16 01:37:45 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
X
X * tar.h: Use new criteria for STDC version of msg.
X
XSat Nov 2 21:31:57 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
X
X * create.c, gnu.c, tar.c: Use DIRENT instead of NDIR to select
X between dirent.h and ndir.h for USG.
X
X * port.c: Rename WANT_FOO to FOO_MISSING to make sharing code
X and configure script with other utilities easier. Use
X VPRINTF_MISSING and DOPRNT_MISSING instead of FOO_MSG to
X select error reporting routines.
X
XThu Oct 17 20:19:02 1991 Michael I Bushnell (mib at churchy.gnu.ai.mit.edu)
X
X * level-0: Repair damage from previous mod: stdin to rsh must
X be the terminal or tar's questions lose.
X
XSat Aug 31 15:05:27 1991 Noah Friedman (friedman at nutrimat.gnu.ai.mit.edu)
X
X * level-0: Fixed several syntax errors associated with
X stdout/stderr redirection.
X Made sure remote host executes commands from sh where redirection
X is necessary, since root's shell might be csh in some places and
X the redirect syntax differs.
X
XThu Aug 29 00:54:01 1991 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * tar.c (long_options). Fixed info-script long option.
X
XMon Aug 26 16:53:50 1991 David J. MacKenzie (djm at pogo.gnu.ai.mit.edu)
X
X * configure, Makefile.in: Only put $< in Makefiles if VPATH
X is being used, because older makes don't understand it.
X
XMon Aug 19 01:47:57 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
X
X * create.c: Indent '#pragma alloca' so non-ANSI compilers
X don't choke on it.
X
XWed Aug 14 14:10:43 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
X
X * list.c (UGSWIDTH): Increase from 11 (sort of like Unix tar) to
X 18, so that with normal user and group names of <= 8 chars,
X the columns never shift in a tar -t listing.
X
XFri Aug 2 00:41:08 1991 David J. MacKenzie (djm at apple-gunkies)
X
X * Makefile.in (dist): Include texinfo.tex and tar.info*.
X (install): Install tar.info*.
X * configure: Set INSTALLDATA.
X
X * configure: Create config.status. Remove it and Makefile if
X interrupted while creating them.
X
X * configure: Check for +srcdir etc. arg and look for
X Makefile.in in that directory. Set VPATH if srcdir is not `.'.
X * Makefile.in: Add `prefix'.
X (tar.info): New target.
X
XTue Jul 30 17:08:04 1991 David J. MacKenzie (djm at apple-gunkies)
X
X * configure: NEED_TZSET has become FTIME_MISSING.
X
XMon Jul 29 19:23:10 1991 David J. MacKenzie (djm at wombat.gnu.ai.mit.edu)
X
X * port.c [F_CHSIZE]: Additional version.
X
XSat Jul 27 22:27:47 1991 David J. MacKenzie (djm at wombat.gnu.ai.mit.edu)
X
X * rmt.h: Clean up ifdefs.
X
X * makefile.pc: Fix typo.
X port.h: Change MSDOS to __MSDOS__.
X [__MSDOS__]: Define off_t. Include io.h and not sys/param.h.
X [__TURBOC__]: Use void * and don't define const.
X
XFri Jul 26 01:12:58 1991 David J. MacKenzie (djm at bleen)
X
X * buffer.c: Rename `eof' to `hit_eof' to avoid conflict with an
X MSDOS function.
X * gnu.c (get_dir_contents): Return NULL, not "\0\0\0\0", on error.
X * diffarch.c (diff_archive): Open files in binary mode.
X Don't use or free a non-malloc'd return value from get_dir_contents.
X * msd_dir.c [__TURBOC__]: Include stdlib.h.
X * rmt.h: lseek returns off_t, not long.
X
X * tar.c (describe): -X is +exclude-from, not +exclude.
X (names_notfound): Free memory only if amiga, not !unix.
X
X * tar.h, tar.c: Add +null option to make -T read
X null-terminated filenames (such as those produced by GNU find
X -print0), and disable -C option.
X This guarantees that odd filenames will get archived.
X * tar.c (read_name_from_file): New function.
X (name_next): Call it instead of fgets.
X
XWed Jul 24 11:17:48 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
X
X * create.c [_AIX]: Declare alloca.
X
X * buffer.c (open_archive): Check for successful open before,
X not after, fstatting the fd.
X
XTue Jul 23 20:51:31 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
X
X * configure: Only define BSD42 if sys/file.h exists.
X If alloca is missing and /usr/ucblib exists (SVR4), use it
X instead of -lPW.
X
X * port.h [!__STDC__]: #define const.
X * gnu.c (dirent_cmp): Fix args to agree with ANSI C prototype.
X * create.c: Declare ck_realloc.
X * gnu.c, diffarch.c: Move check for symlinks to after port.h include.
X
XSat Jul 20 00:03:54 1991 David J. MacKenzie (djm at apple-gunkies)
X
X * msd_dir.[ch]: Use POSIX-style `struct dirent' instead of
X `struct direct'.
X * create.c, gnu.c, tar.c: Adjust callers.
X
XThu Jul 18 00:05:01 1991 David J. MacKenzie (djm at bleen)
X
X * port.c (ck_malloc, ck_realloc): Return PTR, not char *.
X * gnu.c, create.c, tar.c: Fix decls.
X
X * port.c: Don't use the preprocessor to guess missing
X functions on Unix; let configure do it.
X [WANT_GETWD] (getwd): Function removed; not needed because
X getcwd is used if needed.
X * gnu.c, tar.c: Use getcwd if POSIX.
X
X * rtapelib.c: Use SIGTYPE instead of testing SIGNAL_VOID.
X Default to void (more common these days) instead of int.
X
X * tar.c, gnu.c, mangle.c: Remove VOIDSTAR defn. Use PTR instead.
X * port.h: Define PTR.
X
X * gnu.c, tar.c [__MSDOS__ || USG]: Remove incorrect getcwd
X decl; put correct one in port.h [!POSIX].
X
X * tar.c (describe): Print on stdout instead of stderr; it's
X not so much a usage message (since you have to ask for it
X explicitly) as on-line help, and you really need to be able to
X page it because it's more than a screen long.
X
X * Make #ifdefs for sys/file.h or fcntl.h, directory header,
X sys/mtio.h consistent between files. Use NO_MTIO instead of
X tricks with USG and HAVE_MTIO and NO_RMTIOCTL.
X * Move decls of ANSI C and POSIX functions to port.h and
X use standard headers to declare them if available
X [STDC_HEADERS or POSIX].
X * Add many missing function declarations and return types.
X * Some places used __MSDOS__, some MSDOS; standardize on __MSDOS__.
X * Change S_IF macros to S_IS for POSIX.
X * port.h: Define appropriate S_IS macros if missing.
X * port.h: Rename macros for testing exit status to conform to
X POSIX; use the system's versions if available [POSIX].
X * Use POSIX PATH_MAX and NAME_MAX instead of MAXPATHLEN and MAXNAMLEN.
X * port.h: Define PATH_MAX and NAME_MAX.
X * create.c, gnu.c, tar.c: Use ck_malloc and free instead of
X auto arrays of size PATH_MAX or NAME_MAX, since with pathconf
X they might not be constants.
X * Move all definitions of O_* to port.h to reduce redundancy.
X * Make all source files that now need to include port.h do so.
X * port.c: Remove #undefs of WANT_* so you can use -DWANT_*
X when compiling, instead of having to edit port.c.
X [WANT_DUMB_GET_DATE] (get_date): Function removed.
X Even systems without bison can get bison output and compile it.
X [WANT_STRING] (index, rindex, bcopy, bzero, bcmp): Functions
X removed; the translation is now done by macros in port.h.
X * wildmat.c (wildmat): Use POSIX.2 '!' instead of '^' to negate
X character classes.
X
XMon Jul 15 13:47:45 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
X
X * testpad.c (main): Return type void.
X
X * port.c [WANT_STRING]: Don't include memory.h if NO_MEMORY_H.
X
X * create.c (dump_file) [AIX]: Fix typo, `allocate' for `alloca'.
X * gnu.c (collect_and_sort_names): Move misplaced brace out of #ifdef.
X From: Minh Tran-Le <TRANLE@intellicorp.com>.
X
X * configure: Also look in sys/signal.h for signal decl.
X
XWed Jul 10 01:42:55 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
X
X * Rename rtape_server.c to rmt.c and rtape_lib.c to rtapelib.c.
X
X * configure, Makefile.in: $(INSTALLPROG) -> $(INSTALL).
X
XTue Jul 9 01:38:37 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
X
X * Most files: Refer to GPL version 2.
X * COPYING: Use version 2.
X
X * port.c [__TURBOC__] (utime): New function.
X
X * xmalloc: New function (just calls ck_malloc), for alloca.c
X and bison.simple (in getdate.y output).
X
X * Makefile.in (AUX): Include alloca.c and tcexparg.c, a
X command line globber for Turbo C.
X
XMon Jul 8 14:30:52 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
X
X * testpad.c: Open and write to testpad.h instead of stdout,
X because some MS-DOS makes (Borland's at least) can't do
X redirection in commands.
X * Makefile.in: Don't redirect testpad output.
X
XMon Jul 8 12:56:35 1991 Michael I Bushnell (mib at churchy.gnu.ai.mit.edu)
X
X * buffer.c (fl_read): Missing \n in printf.
X
XMon Jul 8 03:40:28 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
X
X * create.c, extract.c, gnu.c, diffarch.c, tar.c: Comment out
X unused variables.
X
X * tar.c (options): Cast get_date arg to VOIDSTAR instead of
X `struct timeb *', since on some non-BSD systems the latter is
X undefined.
X
XSat Jul 6 04:53:14 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
X
X * Replace Makefile with configure, Makefile.in, and makefile.pc.
X Update README with current compilation instructions.
X
X * port.c [WANT_RENAME] (rename): New function.
X
XWed Jul 3 18:10:52 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
X
X * testpad.c (main): Avoid warning from some compilers on array
X address.
X
X * rtape_server.c (sys_errlist): Should be declared extern.
X
XMon Jul 1 14:14:06 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
X
X * Release of version 1.10; appropriate changes to README.
X
X * create.c: Removed printf's about sparse files.
X
X * Fix a misplaced quote in level-0 and change some >& into
X 2>&1.
X
XFri Jun 21 23:04:31 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
X
X * list.c (skip_extended_headers): Userec was being called in
X the wrong place.
X
XThu Jun 20 19:10:35 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
X
X * tar.h: Use ANSI prototypes for msg and msg_perror if
X STDC_MSG is defined, even if BSD42 is also.
X
X * Makefile: Replace DESTDIR with bindir.
X (install): Don't install tar.texinfo. There's no standard
X place for texinfo files, and /usr/local/man is inappropriate.
X Add TAGS, distclean, and realclean targets and SHELL= line.
X
X * version.c: Move old change history to bottom of ChangeLog.
X
XWed Jun 12 12:43:58 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
X
X * rtape_lib.c (__rmt_write): #ifdef should reference
X SIGNAL_VOID, not USG.
X
XWed Jun 5 14:57:11 1991 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * tar.c (name_match, addname): Ugly hack to handle -C without
X any files specified.
X tar.h (struct name): New field for ugly hack.
X
XMon Jun 3 14:46:46 1991 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
X
X * testpad.c: New file to determine if we need special padding
X in struct header in tar.h.
X
X * tar.h (struct header): include padding if necessary, include
X testpad.h.
X
X * Makefile: rules to create testpad.h, etc.
X
XWed May 22 16:02:35 1991 Michael I Bushnell (mib@churchy.gnu.ai.mit.edu)
X
X * tar.c (options): -L takes an argument.
X
X * rtape_lib.c (__rmt_open): add /usr/bin/nsh to the list of
X remote shell programs.
X
X * create.c: define MAXPATHLEN if we don't get it from a system
X header file.
X
X * create.c (deal_with_sparse): return a real return value if
X we can't open the file.
X
X * tar.c (long_options): +newer takes an argument.
X (describe): fix printing in various trivial ways
X
XTue May 21 17:15:19 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
X
X * tar.c (long_options): +get and +concatentate don't require arguments
X
XMon May 20 15:55:30 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
X
X * create.c (write_eot): Don't try and write an EOF if we are
X already at one.
X
X * port.c (strstr): Looking for null string should return zero.
X
XSun May 19 22:30:10 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
X
X * tar.c (options): -l doesn't take an argument
X
X * Makefile: minor fix for SGI 4D defines from torda@scum.ethz.ch
X
X * rtape_server.c (main.c): Suggested mod for 386/AIX from
X Minh Tran-Le. I'm suspicious about this one.
X
X * create.c (dump_file): Mods from Minh Tran-Le for hidden
X files on AIX.
X gnu.c (collect_and_sort_name, get_dir_contents): AIX hidden file mod.
X
X * tar.c: (name_next): Mod from David Taylor to allow -C inside
X a file list given to -T.
X
X * Makefile: Comment describing presence of USE_REXEC.
X
X * extract.c (extract_archive, case LF_SPARSE): zero check for
X last element on numbytes needs to look at value after
X converted from octal.
X
X * port.c: Don't always demand strstr, check for HAVE_STRSTR
X instead.
X Makefile: Comment describing presence of HAVE_STRSTR option.
X
XSun May 19 18:39:48 1991 David J. MacKenzie (djm at churchy.gnu.ai.mit.edu)
X
X * port.c (get_date): Renamed from getdate, to avoid SVR4 conflict.
X * tar.c: Call get_date instead of getdate.
X
XFri May 10 02:58:17 1991 Noah Friedman (friedman at nutrimat)
X
X * tar.c: added "\n\" to the end of some documentation strings
X where they were left off.
X
XThu May 9 17:28:54 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
X
X * Makefile: added level-0, level-1, and backup-specs to AUX.
X * version.c: changed to 1.10 beta.
X * README: updated for 1.10 beta release.
X
XTue Apr 2 12:04:54 1991 Michael I Bushnell (mib at godwin)
X
X * create.c (dump_file): HPUX's st_blocks is in 1024 byte units
X instead of 512 like the rest of the world, so I special cased
X it.
X * tar.c: Undo Noah's changes.
X
XMon Apr 1 17:49:28 1991 Noah Friedman (friedman at wookumz.gnu.ai.mit.edu)
X
X (This ought to be temporary until things are fixed properly. )
X
X * tar.c: (struct option long_options): flag for "sparse" zero if
X compiling under hpux.
X tar.c: (functon options): case 'S' is a no-op if compiling under
X hpux.
X
XSat Mar 30 12:20:41 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
X
X * tar.h: new variable tape_length.
X
X * tar.c (options): add new option +tape-length / -L.
X
X * buffer.c (fl_write): Turn #ifdef TEST code for limited tape
X length on always, for tape-length option.
X
X * create.c (dump_file): avoid apollo lossage where S_IFIFO == S_IFSOCK.
X
X * buffer.c: include regex.h
X * buffer.c (fl_read, open_archive): Use regex routines for
X volume header match.
X * xmalloc.c: removed file; wasn't necessary.
X * tar.c: (main) use ck_malloc instead of xmalloc.
X
XThu Mar 28 04:05:05 1991 Noah Friedman (friedman at goldman)
X
X * regex.c, regex.o: New links.
X * tar.c: include regex.h.
X * Makefile (OBJ2): Add regex.o.
X (regex.o, tar.o): Depend on regex.h
X (SRC2, AUX): Add the new files.
X
XSat Mar 23 15:39:42 1991 Noah Friedman (friedman at wookumz.gnu.ai.mit.edu)
X
X * Makefile: added default flags and options for compiling under
X hpux.
X
X * Added files alloca.c and xmalloc.c
X
XSat Mar 23 14:35:31 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
X
X * port.c: Define WANT_VALLOC in HPUX.
X
XFri Mar 15 06:20:15 1991 David J. MacKenzie (djm at geech.ai.mit.edu)
X
X * rtape_lib.c: If USG and not HAVE_MTIO, define NO_RMTIOCTL
X automatically.
X (_rmt_rexec): Temporarily re-open stdin and stdout to
X /dev/tty, to guarantee that rexec() can prompt and read the
X login name and password from the user.
X From pascal@cnam.cnam.fr (Pascal Meheut).
X * Makefile: Mention -DUSE_REXEC.
X
XFri Mar 8 20:15:11 1991 Michael I Bushnell (mib at wookumz.ai.mit.edu)
X
X * tar.h, Makefile: Makefile CPP macro HAVE_SIZE_T might be
X useful for some people.
X
X * gnu.c: lstat->stat define where appropriate
X
X * buffer.c (fl_write): keep track of amount written for +totals.
X * tar.c, tar.h: set flag f_totals from +totals option
X * tar.h (f_totals, tot_written): new variables
X * tar.c (main): print total written with CMD_CREATE
X
X * tar.c (main): return appropriate exit status
X
XThu Jan 17 00:50:21 1991 David J. MacKenzie (djm at apple-gunkies)
X
X * port.c: Remove a spurious `+' between functions (a remnant
X of a context diff, apparently).
X
XWed Jan 9 19:43:59 1991 Michael I Bushnell (mib at pogo.ai.mit.edu)
X
X * create.c (where_is_data): Rewritten to be better, and then
X #ifdef-ed out.
X (deal_with_sparse): Severly pruned. Now we write or don't
X write only complete blocks, not worrying about partial blocks.
X This simplifies calculations, removes bugs, and elides the
X second scan through the block. The first was zero_record, the
X second was where_is_data.
X
XMon Jan 7 17:13:29 1991 Michael I Bushnell (mib at wookumz.ai.mit.edu)
X
X * create.c (deal_with_sparse): Second computation (for short
X reads) of numbytes increment had subtraction backwards.
X Need to handle calling where_is_data better when we did a
X short read (it might go past the end of the read), also, set
X sparsearray[...].offset in this case too.
X
XFri Jan 4 12:24:38 EST 1991 Jay Fenlason (hack@ai.mit.edu)
X
X * buffer.c Return a special error code if the archive you're
X trying to read starts with a different label than the one specified
X on the command line.
X
XWed Jan 2 12:05:21 EST 1991 Jay Fenlason (hack@ai.mit.edu)
X
X * gnu.c Prepend the current directory to the gnu_dumpfile, so that
X -C's won't affect where the output goes. (sigh.)
X
XTue Dec 18 18:05:59 EST 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * (gnu.c) Don't complain if the gnudumpfile we're reading info
X from doesn't exist.
X
X * create.c Write out gnudumpfile after finishing writing the archive.
X
X * tar.c Add +exclude FNAME, and make +exclude-from do what +exclude
X used to.
X
X Make +version an operation, not an option.
X
X add +confirmation alias for +interactive.
X
XTue Dec 4 13:28:08 EST 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * tar.c (check_exclude) Don't let MUMBLE match MUMBLE.c or fooMUMBLE
X but only foo/MUMBLE
X
X * Add the name mangler (mangle.c, plus changes to create.c and
X extract.c)
X
X * extract.c Three small patches from Chip Salzenberg
X (tct!chip@uunet.uu.net)
X
X Don't complain when extracting a link, IFF it already exists.
X
X Don't complain when extracting a directory IFF it already
X exists.
X
X Don't ad u+wx to directories when running as root.
X
X * gnu.c Some changes from Chip Salzenberg to make
X +listed-incremental work.
X
X * port.c Add the F_FREESP emulation of the ftruncate syscall.
X
XWed Nov 21 15:57:07 EST 1990 Jay Fenlason (hack@ai.mit.edu)
X
X Remove excess \n from lots of msg() calls.
X
XMon Nov 19 14:09:43 EST 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * tar.c Rename +volume to +label
X
XFri Nov 16 15:43:44 1990 David J. MacKenzie (djm at apple-gunkies)
X
X * tar.c (describe): Include the default values for -b and -f
X (as set in the Makefile) in the message.
X
XThu Nov 15 13:36:45 EST 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * extract.c (extract_archive) Do the utime() call before the
X chmod() call, 'cuz some versons of utime() trash the file's mode
X bits.
X
X * list.c (read_and) Call do_something on volume headers and
X multivol files even if they don't match the names we're looking for,
X etc. . .
X
XTue Nov 6 13:51:46 EST 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * port.c (un-quote-string) Don't try to write a null
X if there's already one there.
X
XThu Nov 1 14:58:57 EST 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * buffer.c (new_volume) fflush(msg_file) before reading for
X confirmation on new volume. On EOF or error, print error msg and
X abort.
X
XMon Oct 29 12:06:35 EST 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * getdate.y Use new version of getdate().
X
X * tar.c (name_add) Use sizeof(char *) instead of sizeof(int)
X
X * README give the correct return address.
X
XThu Oct 25 16:03:58 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X rtape_lib.c Change RMTIOCTL to NO_RMTIOCTL, so it is on by default.
X
X rmt.h Add _isrmt() #define for NO_REMOTE case.
X
X gnu.c Add forward reference for add_dir_name().
X
XTue Oct 16 11:04:52 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X 1.09 New -G file implementation of gnu-dump stuff.
X
X * tar.c (name_add) Get the calls to ck_realloc and ck_malloc right.
X
XThu Oct 11 11:23:38 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * gnu.c Fix A couple of typos.
X
XWed Sep 19 13:35:03 1990 David J. MacKenzie (djm at apple-gunkies)
X
X * getdate.y [USG] (ftime): Use `daylight' unless
X DAYLIGHT_MISSING is defined.
X
XMon Sep 17 18:04:21 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * gnu.c (gnu_restore) Don't use a passed char* for the
X file name, use skipcrud+head->header.name, just like everything
X else does. This means that gnu_restore will still work with
X small buffers, etc.
X
XThu Sep 13 15:01:17 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * tar.c (add_exclude) Don't bus-error if the exclude file doesn't
X end with a newline.
X
XSun Sep 9 22:35:27 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
X
X * Makefile (dist): Remove .fname when done.
X
XThu Sep 6 12:48:58 EDT 1990 Jay Fenlason (hack@ai.mti.edu)
X
X * gnu.c (gnu_restore) Rember to skip_file() over the directory
X contents, even if we don't have to do anything with them.
X
X * create.c extract.c diffarch.c Free sparsearray after we're done
X with it.
X
XTue Sep 4 10:18:50 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * Makefile Include gnu.c in dist
X
X * gnu.c move add_dir above read_dir_file so that cc doesn't complain
X about add_dir returning void.
X
XSun Sep 2 20:46:34 1990 David J. MacKenzie (djm at apple-gunkies)
X
X * getdate.y: Declare some more functions and add storage
X classes where omitted to shut compiler up.
X [USG] (ftime): Don't use extern var `daylight'; appears that
X some systems don't have it.
X
XWed Aug 29 00:05:06 1990 David J. MacKenzie (djm at apple-gunkies)
X
X * getdate.y (lookup): In the code that allows `Aug.' to be
X recognized as `Aug', don't chop off the final `.' from words
X like `a.m.', so they can be recognized.
X
XThu Aug 16 11:34:07 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * buffer.c (open_archive) If -O, write verbosity to stderr
X instead of stdout.
X
XFri Aug 10 12:29:28 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * getdate.y Handle an explicit DST in the input string.
X A dozen line patch from Per Foreby (perf@efd.lth.se).
X
XMon Jul 16 13:05:11 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * tar.c rename -g -G +incremental, +listed-imcremental, etc.
X
XFri Jul 13 14:10:33 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * tar.c Make +newer and +newer-mtime work according to their names.
X
X * gnu.c If +newer or +newer-mtime, use the time specified on the
X command line.
X
X * buffer.c, create.c Add test to see if dimwit is trying to
X archive the archive.
X
X * tar.c (long_options[]) re-ordered, so that groups of similar
X options are next to each other. . . I think.
X
X (describe) Modified to more closely reflect reality.
X
XFri Jul 6 13:13:59 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * tar.c add compile-time option for SYS V (?) style
X tape-drive names /dev/rmt/{n}[lmh]
X
X * tar.c Fix getopt-style stuff so that -C always works correctly.
X
X * gnu.c, tar.c make filename to -G optional.
X
X * {all over}, replace some fprintf(stderr...) calls with calls
X to msg().
X
X * port.c Make -Dmumble_MSG option on command line override
X internal assumptions.
X
X * Makefile Mention -Dmumble_MSG options
X
XFri Jul 6 02:35:31 1990 David J. MacKenzie (djm at apple-gunkies)
X
X * tar.c (options): Don't change `c' if it is 0, as getopt now
X handles that internally.
X
XMon Jul 2 15:21:13 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * gnu.c (new file) Moved all the f_gnudump stuff here where we
X can keep track of it easier. Also made -G take a file name where it
X stores the inode information about directories so that we can
X detect moved directores.
X
X * create.c (dump_file) Changed slightly to work with the new
X f_gnudump.
X
X * tar.c Moved the f_gnudump stuff to gnu.c
X
X * tar.c, extract.c added the +do-chown option, which forces tar
X to always try to chown the created files to their original owners.
X
X * version.c New version 1.09
X
XSun Jun 24 14:26:28 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
X
X * create.c: Change ifdefs for directory library header
X selection to be like the ones in tar.c.
X * Makefile [Xenix]: Link with -ldir to get the dirent.h
X directory library.
X
XThu Jun 7 03:31:51 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
X
X * Makefile, buffer.c, diffarch.c: Change MTIO symbol to HAVE_MTIO
X because SCO Xenix defines 'MTIO' for an incompatible tape driver
X system in a file included by termio.h.
X * tar.h: Don't define size_t for Xenix.
X
XTue Jun 5 11:38:00 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * create.c (dump_file) Only print the
X "... is on a different filesystem..." if f_verbose is on.
X also add a case for S_IFSOCK and treat it like a FIFO.
X (Not sure if that's the right thing to do or not, but it's better
X than all those Unknown File Type msgs.)
X
XThu May 31 19:25:36 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * port.c Use #ifdef sparc instead of #ifdef SPARC since
X the lowercase version is defined, and the uppercase one isn't.
X
XTue May 22 11:49:18 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * port.c (ck_malloc) if size==0 pretend size=1
X (ck_realloc) if(!ptr) call ck_malloc instead.
X
XTue May 15 12:05:45 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * diffarch.c (diff_archive) If not f_absolute_paths, and attempt to
X open a file listed in the archive fails, try /filename also. This will
X allow diff to open the wrong file if both /filename and filename exist,
X but there's nothing we can do about that.
X
XFri May 11 16:17:43 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * Makefile, Descripbe new -DMTIO option.
X
X * buffer.c diffarch.c Change ifdefs slightly, so that
X -DMTIO will include sys/mtio.h even if USG is defined.
X This is for HUPX and similar BSD/USG crossovers.
X
XTue May 8 13:14:54 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X * update.c (update_archive) Call reset_eof() when appropriate.
X
X * buffer.c (reset_eof) New function, that turns of EOF flag, and
X re-sets the ar_record and ar_last pointers. This will allow
X 'tar rf non-existant-file' to not core-dump.
X
XFri May 4 14:05:31 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
X
X * tar.c: Recognize the +sparse option. It was documented, but
X only the short form (-S) was actually recognized.
X
XTue Apr 17 21:34:14 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * create.c Don't access location 0 if ->dir_contents is null.
X
XWed Apr 11 17:30:03 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * buffer.c (flush_archive, close_archive, new_volume) Always check
X the return value of rmtclose(), and only give a warning msg if it is
X <0. Some device drivers (including Sun floppy disk, and HP
X streaming tape) return -1 after an IO error (or something like that.)
X
XFri Mar 23 00:06:30 1990 Jim Kingdon (kingdon at mole.ai.mit.edu)
X
X * tar.c (long_options): Make it so +append +extract +list +update
X +catenate and +delete don't take arguments.
X
XMon Mar 12 13:33:53 EST 1990
X
X * buffer.c (open_archive, fl_write) Set the mtime of the volume
X header to the current time.
X
XWed Mar 7 14:10:10 EST 1990 Jay Fenlason (hack@ai.mit.edu)
X
X * buffer.c Fix +compress-block A two character patch from
X Juha Sarlin (juha@tds.kth.se)
X Replace #ifdef __GNU__ with #ifdef __STDC__
X (new_volume) If open of new archive fails, ask again
X (Is probably user error.)
X
X * tar.c Replace #ifdef __GNU__ with #ifdef __STDC__
X
X * port.c Clean up #ifdef and #defines a bit.
X (quote_copy_string) Sometimes the malloc'd buffer
X would be up to two characters too short.
X
X * extract.c (extract_archive) Don't declare ind static.
X
X * create.c (dump_file) Don't declare index_offset static.
X
X * diffarch.c Remove diff_name variable, and always use
X head->header.name, which will always work, unlike diff_name, which
X becomes trash when the next block is read in.
X
XThu Mar 1 13:43:30 EST 1990 Jay Fenlason (hack@wookumz.ai.mit.edu)
X
X * Makefile Mention the -NO_REMOTE option.
X * port.c Fix typo, and define WANT_FTRUNCATE on i386 machines.
X
XMon Feb 26 17:44:53 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu)
X
X * getdate.y: Declare yylex and yyerror as static.
X #define yyparse to getdate_yyparse.
X
XSun Feb 25 20:47:23 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
X
X * tar.c: Remove +old option, since it is a valid abbreviation of
X +old-archive, which does the same thing.
X (describe): A few small cleanups in message.
X
XMon Feb 5 14:29:21 EST 1990 Jay Fenlason (hack@wookumz)
X
X * port.c define LOSING_MSG on sparc, since doprnt_msg doesn't work.
X Fix typo in #ifdef WANT_GETWD
X
XFri Jan 26 16:11:20 EST 1990 Jay Fenlason (hack@wookumz)
X
X 1.08 Sparse file support added. Also various other features.
X
X * diffarch.c (compare_chunk) Include correct arguments in
X a call to fprintf() for an error msg.
X (compare_chunks, compare_dir) First argument is a long, not an int.
X
X * tar.c (options) Use tar variable (argv[0]) as the name to print
X in an error msg, instead of a constant "tar".
X (confirm) Use external variable char TTY_NAME[] for name of file
X to open for confirmation input.
X
X * buffer.c (new_volume) Ditto.
X
X * port.c Add declaration for TTY_NAME[].
X
X * rmt.h Add long declarations for lseek() and __rmt_lseek();
X
XTue Jan 23 14:06:21 EST 1990 Jay Fenlason (hack@wookumz)
X * tar.c, create.c Create the +newer-mtime option, which is like
X +newer, but only looks for files whose mtime is newer than the
X given date.
X
X * rtape_lib.c Make *both* instances of signal-handler stuff use
X void (*foo)() on USG systems.
X
XThu Jan 11 14:03:45 EST 1990 Jay Fenlason (hack@wookumz)
X
X * getdate.y Parse European dates of the form YYMMDD.
X In ftime() Init timezone by calling localtime(), and remember that
X timezone is in seconds, but we want timeb->timezone to be in minutes.
X This small patch from Joergen Haegg (jh@aahas.se)
X
X * rtape_lib.c (__rmt_open) Also look for /usr/bsd/rsh.
X Declare signal handler as returning void instead of int if USG is
X defined.
X
X * port.c Declare WANT_GETWD for SGI 4-D IRIS.
X
X * Makefile Include defines for SGI 4D version. There are a simple
X patch from Mike Muuss (mike@brl.mil).
X
X * buffer.c (fl_read) Work properly on broken Ultrix systems where
X read() returns -1 with errno==ENOSPC on end of tape. Correctly go
X on to the next volume if f_multivol.
X
X * list.c (list_archive,print_header) Flush msg_file after printing
X messages.
X
X * port.c Delete unused references to alloca().
X Don't crash if malloc() returns zero in quote_copy_string.
X Flush stderr in msg() and msg_perror().
X
X * tar.c Flush msg_file after printing confirmation msg.
X
XWed Jan 10 01:58:46 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu)
X
X * tar.c (main): Change -help option and references to it to +help,
X and remove suggestion to run info (which is unreleased, so not
X likely to be of any help).
X
XTue Jan 9 16:16:00 EST 1990 Jay Fenlason (hack @wookumz)
X
X * create.c (dump_file) Close file descriptor if start_header()
X fails.
X (dump_file) Change test for ./ ness to not think that
X .{any character} is a ./ These are both trivial changes from
X Piercarlo "Peter" Grandi pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk
X
X * diffarch.c (diff_init) Print correct number of bytes in error
X message.
X
XTue Jan 9 03:19:49 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu)
X
X * Makefile: Add comment at top noting that two source files also
X contain #defines that might need to be changed by hand.
X
X * create.c, diffarch.c, extract.c: Change L_SET to 0 in lseek
X calls, because only BSD defines it.
X * create.c (dump_file): Make sparse file checking code conditional
X on BSD42 because it uses st_blocks, which the other systems lack.
X
XTue Jan 2 13:35:56 EST 1990 Jay Fenlason (hack@gnu)
X
X * port.c (quote_copy_string) Fix so it doesn't scramble memory if
X the last character is non-printable. A trivial fix from Kian-Tat Lim
X (ktl@wag240.caltech.edu).
X
XTue Dec 19 11:19:37 1989 Jim Kingdon (kingdon at pogo)
X
X * port.c [BSD42]: Define DOPRNT_MSG.
X tar.h [BSD42]: Do not prototype msg{,_perror}.
X
XFri Dec 8 11:02:47 EST 1989 Jay Fenlason (hack@gnu)
X
X * create.c (dump_file) Remove typo in msg.
X
XFri Dec 1 19:26:47 1989 David J. MacKenzie (djm at trix)
X
X * Makefile: Remove comments referring to certain systems lacking
X getopt, since it is now provided always and needed by all systems.
X
X * port.c: Remove copy of getopt.c, as it is now linked in
X separately to always get the current version.
X
X * tar.c: Rename +cat-tars option to +catenate or +concatenate,
X and +local-filesystem to +one-file-system (preferred by rms
X and used in GNU cp for the same purpose).
X (describe): Reflect changes.
X
XTue Nov 28 04:28:26 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu)
X
X * port.c: Move declaration of alloca into #else /* sparc */
X so it will compile on sparcs.
X
XMon Nov 27 15:17:08 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu)
X
X * tar.c (options): Remove -version option (replaced by +version).
X (describe): Mention long options.
X
XSat Nov 25 04:25:23 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu)
X
X * getoldopt.c (getoldopt): Make `opt_index' argument a pointer to
X an int, not char.
X
X * tar.c: Modify long options per rms's suggestions:
X Make preserve-permissions an alias for same-permissions.
X Make preserve-order an alias for same-order.
X Define preserve to mean both of those combined.
X Make old an alias for old-archive.
X Make portability an alias for old-archive, also.
X Rename sym-links to dereference.
X Rename gnudump to incremental.
X Rename filename to file.
X Make compare an alias for diff. Leave diff but prefer compare.
X Rename blocking-factor to block-size.
X Rename chdir to directory.
X Make uncompress an alias for compress.
X Rename confirm to interactive.
X Make get an alias for extract.
X Rename volume-header to volume.
X
X Also make +version an alias for -version.
X
X (options): Shorten code that interprets long options by using
X the equivalent short options' code. This also makes it tons
X easier to change the long options.
X
X (describe): Make usage message more internally consistent
X stylistically.
X
XMon Nov 20 14:55:39 EST 1989 hack@ai.mit.edu
X
X * list.c (read_and) Call check_exclude() to see if the files
X should be skipped on extract or list.
X
XThu Nov 9 18:59:32 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
X
X * buffer.c (fl_read): Fix typos in error message
X "tar EOF not on block boundary".
X
XMon Oct 23 13:09:40 EDT 1989 (hack@ai.mit.edu)
X
X * tar.c (long_options[]) Add an option for blocked compression.
X
XThu Oct 19 13:38:16 EDT 1989 (hack@ai.mit.edu)
X
X * buffer.c (writeerror) Print a more useful error msg.
X
XWed Sep 27 18:33:41 EDT 1989 (hack@ai.mit.edu)
X
X * tar.c (main) Mention "tar -help" if the luser types a non-workable
X set of options.
X
XMon Sep 11 15:03:29 EDT 1989 (hack@ai.mit.edu)
X
X * tar.c (options) Have -F correctly set info_script.
X
XTue Aug 29 12:58:06 EDT 1989 (hack@ai.mit.edu)
X
X * Makefile Include ChangeLog in tar.tar and tar.tar.Z
X
XMon Aug 28 17:42:24 EDT 1989 (hack@ai.mit.edu)
X
X * tar.c (options) Made -F imply -M
X Also remind tar that the -f option takes an argument!
X
X * Modified -F option to make it do what (I think) it
X should. e.g, if you say -F, tar won't send a msg to
X msg_file and wait for a <return> It'll just run the program
X it was given, and when the prog returns, the new tape had
X *better* be ready. . .
X
X * buffer.c (open_archive) Give error message and abort if
X the luser didn't give an archive name.
X
XFri Aug 25 20:05:27 EDT 1989 Joy Kendall (jak at hobbes)
X
X * Added code to make a new option to run a specified script
X at the end of each tape in a multi-volume backup. Changed:
X tar.c: made new switch, -F, and new long-named option,
X "info-script". Code is where you would expect.
X tar.h: added flag f_run_script_at_end, and an extern char *
X called info_script, which optarg gets set to.
X buffer.c: line 1158 in new_volume(): if f_run_script_at_end
X is set, we give info_script to system(), otherwise we do
X what we've always done. **FIXME** I'm not sure if that's all
X that has to be done here.
X
XThu Aug 24 10:09:38 EDT 1989 Joy Kendall (jak at spiff)
X(These changes made over the course of 6/89 - 8/89)
X
X * diffarch.c: diff_archive: Added switches for LF_SPARSE in the
X case statements that needed it. Also, skip any extended headers
X if we need to when we skip over a file. (need to change
X the bit about, if the size doesn't agree AND the file is NOT
X sparse, then there's a discrepancy, because I added another
X field to the header which should be able to deal with the
X sizes) If the file is sparse, call the added routine
X "diff_sparse_files" to compare. Also added routine
X "fill_in_sparse_array".
X
X * extract.c: extract_archive: added the switch LF_SPARSE
X to the case statement as needed, and code to treat the
X sparse file. At label "again_file", modified opening the
X file to see if we should have O_APPEND be one of the modes.
X Added code at label "extract_file" to call the new routine
X "extract_sparse_file" when we have an LF_SPARSE flag.
X
X Note: really should erase the commented-out code in there,
X because it's confusing.
X
X * update.c: made sure that if a file needed to be "skipped"
X over, it would check to see if the linkflag was sparse, and
X if so, would then make sure to skip over any "extended
X headers" that might come after the header itself. Do so by
X calling "skip_extended_headers".
X
X * create.c: create_archive: added code to detect a sparse
X file when in the long case statement. Added ways to detect
X extended headers, and label "extend" (ack! should get rid of
X that, is atrocious). Call the new routine "finish_sparse_file"
X if the linkflag is LF_SPARSE to write the info to the tape.
X Also added routines "init_sparsearray", "deal_with_sparse",
X "clear_buffer", "where_is_data", "zero_record", and
X "find_new_file_size".
X
X * tar.h: Added the #define's SPARSE_EXT_HDR and
X SPARSE_IN_HDR. Added the struct sparse and the struct
X sp_array. Added the linkflag LF_SPARSE. Changed the tar
X header in several ways:
X - added an array of struct sparse's SPARSE_IN_HDR long
X - added a char flag isextended
X - added a char string realsize to store the true
X size of a sparse file
X Added another choice to the union record called a
X struct extended_header, which is an array of 21 struct
X sparse's and a char isextended flag. Added flag
X f_sparse_file to list of flags.
X
X * tar.c: added long-named options to make tar compatible with
X getopt_long, changed Makefile.
X
X... ... .. ..:..:.. ... .... Jay Fenlason (hack@ai.mit.edu)
X
X 1.07 New version to go on beta tape with GCC 1.35
X Better USG support. Also support for __builtin_alloca
X if we're compiling with GCC.
X diffarch.c: Include the correct header files so MTIOCTOP
X is defined.
X tar.c: Don't print the verbose list of options unless
X given -help. The list of options is *way* too long.
X
X 1.06 Use STDC_MSG if __STDC__ defined
X ENXIO meand end-of-volume in archive (for the UNIX PC)
X Added break after volume-header case (line 440) extract.c
X Added patch from arnold@unix.cc.emory.edu to rtape_lib.c
X Added f_absolute_paths option.
X Deleted refereces to UN*X manual sections (dump(8), etc)
X Fixed to not core-dump on illegal options
X Modified msg_perror to call perror("") instead of perror(0)
X patch so -X - works
X Fixed tar.c so 'tar cf - -C dir' doesn't core-dump
X tar.c (name_match): Fixed to chdir() to the appropriate
X directory if the matching name's change_dir is set. This
X makes tar xv -C foo {files} work.
X
X 1.05 A fix to make confirm() work when the archive is on stdin
X include 'extern FILE *msg_file;' in pr_mkdir(), and fix
X tar.h to work with __STDC__
X
X Added to port.c: mkdir() ftruncate() Removed: lstat()
X Fixed -G to work with -X
X Another fix to tar.texinfo
X Changed tar.c to say argv[0]":you must specify exactly ...
X buffer.c: modified child_open() to keep tar from hanging when
X it is done reading/writing a compressed archive
X added fflush(msg_file) before printing error messages
X create.c: fixed to make link_names non-absolute
X
X 1.04 Added functions msg() and msg_perror() Modified all the
X files to call them. Also checked that all (I hope)
X calls to msg_perror() have a valid errno value
X (modified anno() to leave errno alone), etc
X Re-fixed the -X option. This time for sure. . .
X re-modified the msg stuff. flushed anno() completely
X Modified the directory stuff so it should work on sysV boxes
X added ftime() to getdate.y
X Fixed un_quote_string() so it won't wedge on \" Also fixed
X \ddd (like \123, etc)
X More fixes to tar.texinfo
X
X 1.03 Fixed buffer.c so 'tar tzf NON_EXISTENT_FILE' returns an error
X message instead of hanging forever
X More fixes to tar.texinfo
X
X 1.02 Fixed tar.c so 'tar -h' and 'tar -v' don't cause core dump
X Also fixed the 'usage' message to be more up-to-date.
X Fixed diffarch.c so verify should compile without MTIOCTOP
X defined
X
X 1.01 Fixed typoes in tar.texinfo
X Fixed a bug in the #define for rmtcreat()
X Fixed the -X option to not call realloc() of 0.
X
X Version 1.00: version.c added. -version option added
X Installed new version of the remote-tape library
X Added -help option
X
XLocal Variables:
Xmode: indented-text
Xleft-margin: 8
Xversion-control: never
XEnd:
END_OF_FILE
if test 61978 -ne `wc -c <'ChangeLog'`; then
echo shar: \"'ChangeLog'\" unpacked with wrong size!
fi
# end of 'ChangeLog'
fi
if test -f 'Makefile.in' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Makefile.in'\"
else
echo shar: Extracting \"'Makefile.in'\" \(6306 characters\)
sed "s/^X//" >'Makefile.in' <<'END_OF_FILE'
X# Un*x Makefile for GNU tar program.
X# Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
X
X# This program is free software; you can redistribute it and/or modify
X# it under the terms of the GNU General Public License as published by
X# the Free Software Foundation; either version 2, or (at your option)
X# any later version.
X
X# This program is distributed in the hope that it will be useful,
X# but WITHOUT ANY WARRANTY; without even the implied warranty of
X# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X# GNU General Public License for more details.
X
X# You should have received a copy of the GNU General Public License
X# along with this program; if not, write to the Free Software
X# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X#### Start of system configuration section. ####
X
Xsrcdir = @srcdir@
XVPATH = @srcdir@
X
X# If you use gcc, you should either run the fixincludes script that
X# comes with it or else use gcc with the -traditional option. Otherwise
X# ioctl calls will be compiled incorrectly on some systems.
XCC = @CC@
XYACC = @YACC@
XINSTALL = @INSTALL@
XINSTALL_PROGRAM = @INSTALL_PROGRAM@
XINSTALL_DATA = @INSTALL_DATA@
X
X# Things you might add to DEFS:
X# -DSTDC_HEADERS If you have ANSI C headers and libraries.
X# -DHAVE_UNISTD_H If you have unistd.h.
X# -DHAVE_STRING_H If you don't have ANSI C headers but have string.h.
X# -DHAVE_LIMITS_H If you have limits.h.
X# -DBSD42 If you have sys/dir.h (unless you use -DPOSIX),
X# sys/file.h, and st_blocks in `struct stat'.
X# -DDIRENT If you have dirent.h.
X# -DSYSNDIR Old Xenix systems (sys/ndir.h).
X# -DSYSDIR Old BSD systems (sys/dir.h).
X# -DNDIR Old System V systems (ndir.h).
X# -DMAJOR_IN_MKDEV If major, minor, makedev defined in sys/mkdev.h.
X# -DMAJOR_IN_SYSMACROS If major, minor, makedev defined in sys/sysmacros.h.
X# -DRETSIGTYPE=int If your signal handlers return int, not void.
X# -DHAVE_SYS_MTIO_H If you have sys/mtio.h (magtape ioctls).
X# -DHAVE_SYS_GENTAPE_H If you have sys/gentape.h (ISC magtape ioctls).
X# -DHAVE_NETDB_H To use rexec for remote tape operations
X# instead of forking rsh or remsh.
X# -DNO_REMOTE If you have neither a remote shell nor rexec.
X# -DHAVE_VPRINTF If you have vprintf function.
X# -DHAVE_DOPRNT If you have _doprnt function (but lack vprintf).
X# -DHAVE_FTIME If you have ftime system call.
X# -DHAVE_STRSTR If you have strstr function.
X# -DHAVE_VALLOC If you have valloc function.
X# -DHAVE_MKDIR If you have mkdir and rmdir system calls.
X# -DHAVE_MKNOD If you have mknod system call.
X# -DHAVE_RENAME If you have rename system call.
X# -DHAVE_GETCWD If not POSIX.1 but have getcwd function.
X# -DHAVE_FTRUNCATE If you have ftruncate system call.
X# -DV7 On Version 7 Unix (not tested in a long time).
X# -DEMUL_OPEN3 If you lack a 3-argument version of open, and want
X# to emulate it with system calls you do have.
X# -DNO_OPEN3 If you lack the 3-argument open and want to
X# disable the tar -k option instead of emulating open.
X# -DXENIX If you have sys/inode.h and need it to be included.
X
XDEF_AR_FILE = @DEF_AR_FILE@
XDEFBLOCKING = 20
XDEFS = @DEFS@ -DDEF_AR_FILE=\"$(DEF_AR_FILE)\" -DDEFBLOCKING=$(DEFBLOCKING)
X
X# Set this to rtapelib.o unless you defined NO_REMOTE, in which case
X# make it empty.
XRTAPELIB = @RTAPELIB@
XLIBS = @LIBS@
X
XCFLAGS = -g
XLDFLAGS = -g
X
Xprefix = /usr/local
Xexec_prefix = $(prefix)
X
X# Prefix for each installed program, normally empty or `g'.
Xbinprefix =
X
X# The directory to install tar in.
Xbindir = $(exec_prefix)/bin
X
X# Where to put the rmt executable.
Xlibdir = /etc
X
X# The directory to install the info files in.
Xinfodir = $(prefix)/info
X
X#### End of system configuration section. ####
X
XSHELL = /bin/sh
X
XSRC1 = tar.c create.c extract.c buffer.c getoldopt.c update.c gnu.c mangle.c
XSRC2 = version.c list.c names.c diffarch.c port.c fnmatch.c getopt.c malloc.c
XSRC3 = getopt1.c regex.c getdate.y getdate.c alloca.c
XSRCS = $(SRC1) $(SRC2) $(SRC3)
XOBJ1 = tar.o create.o extract.o buffer.o getoldopt.o update.o gnu.o mangle.o
XOBJ2 = version.o list.o names.o diffarch.o port.o fnmatch.o getopt.o @MALLOC@
XOBJ3 = getopt1.o regex.o getdate.o $(RTAPELIB) @ALLOCA@
XOBJS = $(OBJ1) $(OBJ2) $(OBJ3)
XAUX = README INSTALL NEWS COPYING ChangeLog Makefile.in makefile.pc \
X configure configure.in \
X tar.h fnmatch.h pathmax.h port.h open3.h getopt.h regex.h \
X rmt.h rmt.c rtapelib.c \
X msd_dir.h msd_dir.c tcexparg.c \
X level-0 level-1 backup-specs dump-remind testpad.c getpagesize.h
X# tar.texinfo tar.info* texinfo.tex \
X
Xall: @PROGS@
X# tar.info
X
X.c.o:
X $(CC) -c $(CFLAGS) $(CPPFLAGS) $(DEFS) -I$(srcdir) -I. $<
X
Xtar: $(OBJS)
X $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
X
Xrmt: rmt.c
X $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(srcdir)/rmt.c $(LIBS)
X
Xtar.info: tar.texinfo
X makeinfo $(srcdir)/tar.texinfo
X
Xinstall: all
X $(INSTALL_PROGRAM) tar $(bindir)/$(binprefix)tar
X -test ! -f rmt || $(INSTALL_PROGRAM) rmt $(libdir)/rmt
X# for file in $(srcdir)/tar.info*; \
X# do $(INSTALL_DATA) $$file $(infodir)/$$file; \
X# done
X
Xuninstall:
X rm -f $(bindir)/$(binprefix)tar $(infodir)/tar.info*
X -rm -f $(libdir)/rmt
X
X$(OBJS): tar.h pathmax.h port.h testpad.h
Xregex.o buffer.o tar.o: regex.h
Xtar.o fnmatch.o: fnmatch.h
X
Xgetdate.c: getdate.y
X $(YACC) $(srcdir)/getdate.y
X mv y.tab.c getdate.c
X# getdate.y has 8 shift/reduce conflicts.
X
Xtestpad.h: testpad
X ./testpad
X
Xtestpad: testpad.o
X $(CC) -o $@ testpad.o
X
XTAGS: $(SRCS)
X etags $(SRCS)
X
Xclean:
X rm -f *.o tar rmt testpad testpad.h core
X
Xmostlyclean: clean
X
Xdistclean: clean
X rm -f Makefile config.status
X
Xrealclean: distclean
X rm -f TAGS *.info* getdate.c y.tab.c
X
Xshar: $(SRCS) $(AUX)
X shar $(SRCS) $(AUX) | gzip > tar-`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q version.c`.shar.z
X
Xdist: $(SRCS) $(AUX)
X echo tar-`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q version.c` > .fname
X -rm -rf `cat .fname`
X mkdir `cat .fname`
X for file in $(SRCS) $(AUX); do \
X ln $$file `cat .fname` || cp $$file `cat .fname`; done
X tar chzf `cat .fname`.tar.z `cat .fname`
X -rm -rf `cat .fname` .fname
X
Xtar.zoo: $(SRCS) $(AUX)
X -rm -rf tmp.dir
X -mkdir tmp.dir
X -rm tar.zoo
X for X in $(SRCS) $(AUX) ; do echo $$X ; sed 's/$$//' $$X > tmp.dir/$$X ; done
X cd tmp.dir ; zoo aM ../tar.zoo *
X -rm -rf tmp.dir
X
X# Prevent GNU make v3 from overflowing arg limit on SysV.
X.NOEXPORT:
END_OF_FILE
if test 6306 -ne `wc -c <'Makefile.in'`; then
echo shar: \"'Makefile.in'\" unpacked with wrong size!
fi
# end of 'Makefile.in'
fi
if test -f 'makefile.pc' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'makefile.pc'\"
else
echo shar: Extracting \"'makefile.pc'\" \(1700 characters\)
sed "s/^X//" >'makefile.pc' <<'END_OF_FILE'
X# Makefile for GNU tar on MS-DOS.
X# Copyright (C) 1991 Free Software Foundation, Inc.
X
X# This program is free software; you can redistribute it and/or modify
X# it under the terms of the GNU General Public License as published by
X# the Free Software Foundation; either version 2, or (at your option)
X# any later version.
X
X# This program is distributed in the hope that it will be useful,
X# but WITHOUT ANY WARRANTY; without even the implied warranty of
X# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X# GNU General Public License for more details.
X
X# You should have received a copy of the GNU General Public License
X# along with this program; if not, write to the Free Software
X# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
XCC = bcc
XRM = rm -f
X
XMODEL = m
XDEFS = -DNONAMES -DNO_REMOTE -DSTDC_HEADERS=1 -m$(MODEL) -Dmain=_main
XLIBS =
X
XCFLAGS = $(DEFS)
XLDFLAGS = -m$(MODEL)
X
XOBJ1 = tar.obj create.obj extract.obj buffer.obj getoldopt.obj update.obj gnu.obj mangle.obj
XOBJ2 = version.obj list.obj names.obj diffarch.obj port.obj fnmatch.obj getopt.obj
XOBJ3 = getopt1.obj regex.obj getdate.obj alloca.obj tcexparg.obj msd_dir.obj
XOBJS = $(OBJ1) $(OBJ2) $(OBJ3)
X
Xall: tar
X
Xtar: testpad.h getdate.c $(OBJS)
X $(RM) testpad.obj
X $(CC) $(LDFLAGS) -etar *.obj $(LIBS)
X
X.c.obj:
X $(CC) -c $(CFLAGS) $<
X
X# For some reason, Borland C++ 3.1 chokes on this file when given
X# the full set of -D options.
Xgetoldopt.obj: getoldopt.c
X $(CC) -c -m$(MODEL) -DSTDC_HEADERS getoldopt.c
X
Xtestpad.h: testpad.exe
X testpad
X
Xtestpad.exe: testpad.c
X $(CC) $(LDFLAGS) -etestpad testpad.c $(LIBS)
X
Xclean:
X $(RM) errs *.obj tar testpad testpad.h
X
Xmostlyclean: clean
X
Xdistclean: clean
X
Xrealclean: clean
END_OF_FILE
if test 1700 -ne `wc -c <'makefile.pc'`; then
echo shar: \"'makefile.pc'\" unpacked with wrong size!
fi
# end of 'makefile.pc'
fi
if test -f 'configure' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'configure'\"
else
echo shar: Extracting \"'configure'\" \(19657 characters\)
sed "s/^X//" >'configure' <<'END_OF_FILE'
X#!/bin/sh
X# Guess values for system-dependent variables and create Makefiles.
X# Generated automatically using autoconf.
X# Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
X
X# This program is free software; you can redistribute it and/or modify
X# it under the terms of the GNU General Public License as published by
X# the Free Software Foundation; either version 2, or (at your option)
X# any later version.
X
X# This program is distributed in the hope that it will be useful,
X# but WITHOUT ANY WARRANTY; without even the implied warranty of
X# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X# GNU General Public License for more details.
X
X# You should have received a copy of the GNU General Public License
X# along with this program; if not, write to the Free Software
X# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X# Usage: configure [--srcdir=DIR] [--host=HOST] [--gas] [--nfp] [--no-create]
X# [--prefix=PREFIX] [--exec-prefix=PREFIX] [--with-PACKAGE] [TARGET]
X# Ignores all args except --srcdir, --prefix, --exec-prefix, --no-create, and
X# --with-PACKAGE unless this script has special code to handle it.
X
X
Xfor arg
Xdo
X # Handle --exec-prefix with a space before the argument.
X if test x$next_exec_prefix = xyes; then exec_prefix=$arg; next_exec_prefix=
X # Handle --host with a space before the argument.
X elif test x$next_host = xyes; then next_host=
X # Handle --prefix with a space before the argument.
X elif test x$next_prefix = xyes; then prefix=$arg; next_prefix=
X # Handle --srcdir with a space before the argument.
X elif test x$next_srcdir = xyes; then srcdir=$arg; next_srcdir=
X else
X case $arg in
X # For backward compatibility, also recognize exact --exec_prefix.
X -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* | --exec=* | --exe=* | --ex=* | --e=*)
X exec_prefix=`echo $arg | sed 's/[-a-z_]*=//'` ;;
X -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- | --exec | --exe | --ex | --e)
X next_exec_prefix=yes ;;
X
X -gas | --gas | --ga | --g) ;;
X
X -host=* | --host=* | --hos=* | --ho=* | --h=*) ;;
X -host | --host | --hos | --ho | --h)
X next_host=yes ;;
X
X -nfp | --nfp | --nf) ;;
X
X -no-create | --no-create | --no-creat | --no-crea | --no-cre | --no-cr | --no-c | --no- | --no)
X no_create=1 ;;
X
X -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
X prefix=`echo $arg | sed 's/[-a-z_]*=//'` ;;
X -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
X next_prefix=yes ;;
X
X -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=* | --s=*)
X srcdir=`echo $arg | sed 's/[-a-z_]*=//'` ;;
X -srcdir | --srcdir | --srcdi | --srcd | --src | --sr | --s)
X next_srcdir=yes ;;
X
X -with-* | --with-*)
X package=`echo $arg|sed 's/-*with-//'`
X # Delete all the valid chars; see if any are left.
X if test -n "`echo $package|sed 's/[-a-zA-Z0-9_]*//g'`"; then
X echo "configure: $package: invalid package name" >&2; exit 1
X fi
X eval "with_`echo $package|sed s/-/_/g`=1" ;;
X
X *) ;;
X esac
X fi
Xdone
X
Xtrap 'rm -f conftest* core; exit 1' 1 3 15
X
Xrm -f conftest*
Xcompile='${CC-cc} $CFLAGS $DEFS conftest.c -o conftest $LIBS >/dev/null 2>&1'
X
X# A filename unique to this package, relative to the directory that
X# configure is in, which we can look for to find out if srcdir is correct.
Xunique_file=tar.h
X
X# Find the source files, if location was not specified.
Xif test -z "$srcdir"; then
X srcdirdefaulted=yes
X # Try the directory containing this script, then `..'.
X prog=$0
X confdir=`echo $prog|sed 's%/[^/][^/]*$%%'`
X test "X$confdir" = "X$prog" && confdir=.
X srcdir=$confdir
X if test ! -r $srcdir/$unique_file; then
X srcdir=..
X fi
Xfi
Xif test ! -r $srcdir/$unique_file; then
X if test x$srcdirdefaulted = xyes; then
X echo "configure: Can not find sources in \`${confdir}' or \`..'." 1>&2
X else
X echo "configure: Can not find sources in \`${srcdir}'." 1>&2
X fi
X exit 1
Xfi
X# Preserve a srcdir of `.' to avoid automounter screwups with pwd.
X# But we can't avoid them for `..', to make subdirectories work.
Xcase $srcdir in
X .|/*|~*) ;;
X *) srcdir=`cd $srcdir; pwd` ;; # Make relative path absolute.
Xesac
X
XPROGS="tar"
Xif test -z "$CC"; then
X echo checking for gcc
X saveifs="$IFS"; IFS="${IFS}:"
X for dir in $PATH; do
X test -z "$dir" && dir=.
X if test -f $dir/gcc; then
X CC="gcc"
X break
X fi
X done
X IFS="$saveifs"
Xfi
Xtest -z "$CC" && CC="cc"
X
X# Find out if we are using GNU C, under whatever name.
Xcat > conftest.c <<EOF
X#ifdef __GNUC__
X yes
X#endif
XEOF
X${CC-cc} -E conftest.c > conftest.out 2>&1
Xif egrep yes conftest.out >/dev/null 2>&1; then
X GCC=1 # For later tests.
Xfi
Xrm -f conftest*
X
Xecho checking how to run the C preprocessor
Xif test -z "$CPP"; then
X CPP='${CC-cc} -E'
X cat > conftest.c <<EOF
X#include <stdio.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X :
Xelse
X CPP=/lib/cpp
Xfi
Xrm -f conftest*
Xfi
X
Xif test -n "$GCC"; then
X echo checking whether -traditional is needed
X pattern="Autoconf.*'x'"
X prog='#include <sgtty.h>
XAutoconf TIOCGETP'
X cat > conftest.c <<EOF
X$prog
XEOF
Xeval "$CPP $DEFS conftest.c > conftest.out 2>&1"
Xif egrep "$pattern" conftest.out >/dev/null 2>&1; then
X need_trad=1
Xfi
Xrm -f conftest*
X
X
X if test -z "$need_trad"; then
X prog='#include <termio.h>
XAutoconf TCGETA'
X cat > conftest.c <<EOF
X$prog
XEOF
Xeval "$CPP $DEFS conftest.c > conftest.out 2>&1"
Xif egrep "$pattern" conftest.out >/dev/null 2>&1; then
X need_trad=1
Xfi
Xrm -f conftest*
X
X fi
X test -n "$need_trad" && CC="$CC -traditional"
Xfi
X
X# Make sure to not get the incompatible SysV /etc/install and
X# /usr/sbin/install, which might be in PATH before a BSD-like install,
X# or the SunOS /usr/etc/install directory, or the AIX /bin/install,
X# or the AFS install, which mishandles nonexistent args. (Sigh.)
Xif test -z "$INSTALL"; then
X echo checking for install
X saveifs="$IFS"; IFS="${IFS}:"
X for dir in $PATH; do
X test -z "$dir" && dir=.
X case $dir in
X /etc|/usr/sbin|/usr/etc|/usr/afsws/bin) ;;
X *)
X if test -f $dir/install; then
X if grep dspmsg $dir/install >/dev/null 2>&1; then
X : # AIX
X else
X INSTALL="$dir/install -c"
X INSTALL_PROGRAM='$(INSTALL)'
X INSTALL_DATA='$(INSTALL) -m 644'
X break
X fi
X fi
X ;;
X esac
X done
X IFS="$saveifs"
Xfi
XINSTALL=${INSTALL-cp}
XINSTALL_PROGRAM=${INSTALL_PROGRAM-'$(INSTALL)'}
XINSTALL_DATA=${INSTALL_DATA-'$(INSTALL)'}
X
Xif test -z "$YACC"; then
X echo checking for bison
X saveifs="$IFS"; IFS="${IFS}:"
X for dir in $PATH; do
X test -z "$dir" && dir=.
X if test -f $dir/bison; then
X YACC="bison -y"
X break
X fi
X done
X IFS="$saveifs"
Xfi
Xtest -z "$YACC" && YACC=""
X
Xif test -z "$YACC"; then
X echo checking for byacc
X saveifs="$IFS"; IFS="${IFS}:"
X for dir in $PATH; do
X test -z "$dir" && dir=.
X if test -f $dir/byacc; then
X YACC="byacc"
X break
X fi
X done
X IFS="$saveifs"
Xfi
Xtest -z "$YACC" && YACC="yacc"
X
X
Xecho checking for AIX
Xcat > conftest.c <<EOF
X#ifdef _AIX
X yes
X#endif
X
XEOF
Xeval "$CPP $DEFS conftest.c > conftest.out 2>&1"
Xif egrep "yes" conftest.out >/dev/null 2>&1; then
X DEFS="$DEFS -D_ALL_SOURCE=1"
Xfi
Xrm -f conftest*
X
X
Xecho checking for minix/config.h
Xcat > conftest.c <<EOF
X#include <minix/config.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X MINIX=1
Xfi
Xrm -f conftest*
X
X# The Minix shell can't assign to the same variable on the same line!
Xif test -n "$MINIX"; then
X DEFS="$DEFS -D_POSIX_SOURCE=1"
X DEFS="$DEFS -D_POSIX_1_SOURCE=2"
X DEFS="$DEFS -D_MINIX=1"
Xfi
X
Xecho checking for POSIXized ISC
Xif test -d /etc/conf/kconfig.d &&
X grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1
Xthen
X ISC=1 # If later tests want to check for ISC.
X DEFS="$DEFS -D_POSIX_SOURCE=1"
X if test -n "$GCC"; then
X CC="$CC -posix"
X else
X CC="$CC -Xp"
X fi
Xfi
X
Xecho checking for return type of signal handlers
Xcat > conftest.c <<EOF
X#include <sys/types.h>
X#include <signal.h>
X#ifdef signal
X#undef signal
X#endif
Xextern void (*signal ()) ();
Xmain() { exit(0); }
Xt() { int i; }
XEOF
Xif eval $compile; then
X DEFS="$DEFS -DRETSIGTYPE=void"
Xelse
X DEFS="$DEFS -DRETSIGTYPE=int"
Xfi
Xrm -f conftest*
X
X
Xecho checking for size_t in sys/types.h
Xecho '#include <sys/types.h>' > conftest.c
Xeval "$CPP $DEFS conftest.c > conftest.out 2>&1"
Xif egrep "size_t" conftest.out >/dev/null 2>&1; then
X :
Xelse
X DEFS="$DEFS -Dsize_t=unsigned"
Xfi
Xrm -f conftest*
X
Xecho checking for major, minor and makedev header
Xcat > conftest.c <<EOF
X#include <sys/types.h>
Xmain() { exit(0); }
Xt() { return makedev(0, 0); }
XEOF
Xif eval $compile; then
X makedev=1
Xfi
Xrm -f conftest*
X
Xif test -z "$makedev"; then
Xecho checking for sys/mkdev.h
Xcat > conftest.c <<EOF
X#include <sys/mkdev.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X DEFS="$DEFS -DMAJOR_IN_MKDEV=1" makedev=1
Xfi
Xrm -f conftest*
X
Xfi
Xif test -z "$makedev"; then
Xecho checking for sys/sysmacros.h
Xcat > conftest.c <<EOF
X#include <sys/sysmacros.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X DEFS="$DEFS -DMAJOR_IN_SYSMACROS=1"
Xfi
Xrm -f conftest*
X
Xfi
X
Xecho checking for directory library header
Xecho checking for dirent.h
Xcat > conftest.c <<EOF
X#include <dirent.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X DEFS="$DEFS -DDIRENT=1" dirheader=dirent.h
Xfi
Xrm -f conftest*
X
Xif test -z "$dirheader"; then
Xecho checking for sys/ndir.h
Xcat > conftest.c <<EOF
X#include <sys/ndir.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X DEFS="$DEFS -DSYSNDIR=1" dirheader=sys/ndir.h
Xfi
Xrm -f conftest*
X
Xfi
Xif test -z "$dirheader"; then
Xecho checking for sys/dir.h
Xcat > conftest.c <<EOF
X#include <sys/dir.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X DEFS="$DEFS -DSYSDIR=1" dirheader=sys/dir.h
Xfi
Xrm -f conftest*
X
Xfi
Xif test -z "$dirheader"; then
Xecho checking for ndir.h
Xcat > conftest.c <<EOF
X#include <ndir.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X DEFS="$DEFS -DNDIR=1" dirheader=ndir.h
Xfi
Xrm -f conftest*
X
Xfi
X
Xecho checking for closedir return value
Xcat > conftest.c <<EOF
X#include <sys/types.h>
X#include <$dirheader>
Xint closedir(); main() { exit(0); }
XEOF
Xeval $compile
Xif test -s conftest && (./conftest; exit) 2>/dev/null; then
X :
Xelse
X DEFS="$DEFS -DVOID_CLOSEDIR=1"
Xfi
Xrm -f conftest*
X
X# The 3-argument open happens to go along with the O_* defines,
X# which are easier to check for.
Xecho checking for fcntl.h
Xcat > conftest.c <<EOF
X#include <fcntl.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X open_header=fcntl.h
Xelse
X open_header=sys/file.h
Xfi
Xrm -f conftest*
X
Xecho checking for 3-argument open
Xcat > conftest.c <<EOF
X#include <$open_header>
Xmain() { exit(0); }
Xt() { int x = O_RDONLY; }
XEOF
Xif eval $compile; then
X :
Xelse
X DEFS="$DEFS -DEMUL_OPEN3=1"
Xfi
Xrm -f conftest*
X
Xecho checking for remote tape and socket header files
Xecho checking for sys/mtio.h
Xcat > conftest.c <<EOF
X#include <sys/mtio.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X DEFS="$DEFS -DHAVE_SYS_MTIO_H=1" have_mtio=1
Xfi
Xrm -f conftest*
X
Xif test -n "$have_mtio"; then
Xcat > conftest.c <<EOF
X#include <sgtty.h>
X#include <sys/socket.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X PROGS="$PROGS rmt"
Xfi
Xrm -f conftest*
Xfi
X
Xecho checking for remote shell
Xif test -f /usr/ucb/rsh || test -f /usr/bin/remsh || test -f /usr/bin/rsh ||
X test -f /usr/bsd/rsh || test -f /usr/bin/nsh; then
X RTAPELIB=rtapelib.o
Xelse
X echo checking for netdb.h
Xcat > conftest.c <<EOF
X#include <netdb.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X DEFS="$DEFS -DHAVE_NETDB_H=1" RTAPELIB=rtapelib.o
Xelse
X DEFS="$DEFS -DNO_REMOTE=1"
Xfi
Xrm -f conftest*
X
Xfi
X
Xecho checking for ANSI C header files
Xcat > conftest.c <<EOF
X#include <stdlib.h>
X#include <stdarg.h>
X#include <string.h>
X#include <float.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X # SunOS string.h does not declare mem*, contrary to ANSI.
Xecho '#include <string.h>' > conftest.c
Xeval "$CPP $DEFS conftest.c > conftest.out 2>&1"
Xif egrep "memchr" conftest.out >/dev/null 2>&1; then
X # SGI's /bin/cc from Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
Xcat > conftest.c <<EOF
X#include <ctype.h>
X#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
X#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
X#define XOR(e,f) (((e) && !(f)) || (!(e) && (f)))
Xint main () { int i; for (i = 0; i < 256; i++)
Xif (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
Xexit (0); }
X
XEOF
Xeval $compile
Xif test -s conftest && (./conftest; exit) 2>/dev/null; then
X DEFS="$DEFS -DSTDC_HEADERS=1"
Xfi
Xrm -f conftest*
Xfi
Xrm -f conftest*
X
Xfi
Xrm -f conftest*
X
Xecho checking for unistd.h
Xcat > conftest.c <<EOF
X#include <unistd.h>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X DEFS="$DEFS -DHAVE_UNISTD_H=1"
Xfi
Xrm -f conftest*
X
Xecho checking for getgrgid declaration
Xecho '#include <grp.h>' > conftest.c
Xeval "$CPP $DEFS conftest.c > conftest.out 2>&1"
Xif egrep "getgrgid" conftest.out >/dev/null 2>&1; then
X DEFS="$DEFS -DHAVE_GETGRGID=1"
Xfi
Xrm -f conftest*
X
Xecho checking for getpwuid declaration
Xecho '#include <pwd.h>' > conftest.c
Xeval "$CPP $DEFS conftest.c > conftest.out 2>&1"
Xif egrep "getpwuid" conftest.out >/dev/null 2>&1; then
X DEFS="$DEFS -DHAVE_GETPWUID=1"
Xfi
Xrm -f conftest*
X
Xfor hdr in string.h limits.h
Xdo
Xtrhdr=HAVE_`echo $hdr | tr '[a-z]./' '[A-Z]__'`
Xecho checking for ${hdr}
Xcat > conftest.c <<EOF
X#include <${hdr}>
XEOF
Xerr=`eval "$CPP $DEFS conftest.c 2>&1 >/dev/null"`
Xif test -z "$err"; then
X DEFS="$DEFS -D${trhdr}=1"
Xfi
Xrm -f conftest*
Xdone
X
Xecho checking default archive
X# This might guess wrong, but it's not very important.
Xfor dev in rmt8 rmt0 rmt0h rct0 rst0 tape rct/c7d0s2
Xdo
X if test -n "`ls /dev/$dev 2>/dev/null`"; then
X DEF_AR_FILE=/dev/$dev
X break
X fi
Xdone
Xif test -z "$DEF_AR_FILE"; then
X DEF_AR_FILE=-
Xfi
X
Xfor func in strstr valloc mkdir mknod rename ftruncate ftime getcwd
Xdo
Xtrfunc=HAVE_`echo $func | tr '[a-z]' '[A-Z]'`
Xecho checking for ${func}
Xcat > conftest.c <<EOF
X#include <stdio.h>
Xmain() { exit(0); }
Xt() {
X#ifdef __stub_${func}
Xchoke me
X#else
X/* Override any gcc2 internal prototype to avoid an error. */
Xextern char ${func}(); ${func}();
X#endif
X }
XEOF
Xif eval $compile; then
X DEFS="$DEFS -D${trfunc}=1"
Xfi
Xrm -f conftest*
X#endif
Xdone
X
Xecho checking for vprintf
Xcat > conftest.c <<EOF
X
Xmain() { exit(0); }
Xt() { vprintf(); }
XEOF
Xif eval $compile; then
X DEFS="$DEFS -DHAVE_VPRINTF=1"
Xelse
X vprintf_missing=1
Xfi
Xrm -f conftest*
X
Xif test -n "$vprintf_missing"; then
Xecho checking for _doprnt
Xcat > conftest.c <<EOF
X
Xmain() { exit(0); }
Xt() { _doprnt(); }
XEOF
Xif eval $compile; then
X DEFS="$DEFS -DHAVE_DOPRNT=1"
Xfi
Xrm -f conftest*
X
Xfi
X
X# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works
X# for constant arguments. Useless!
Xecho checking for working alloca.h
Xcat > conftest.c <<EOF
X#include <alloca.h>
Xmain() { exit(0); }
Xt() { char *p = alloca(2 * sizeof(int)); }
XEOF
Xif eval $compile; then
X DEFS="$DEFS -DHAVE_ALLOCA_H=1"
Xfi
Xrm -f conftest*
X
Xdecl="#ifdef __GNUC__
X#define alloca __builtin_alloca
X#else
X#if HAVE_ALLOCA_H
X#include <alloca.h>
X#else
X#ifdef _AIX
X #pragma alloca
X#else
Xchar *alloca ();
X#endif
X#endif
X#endif
X"
Xecho checking for alloca
Xcat > conftest.c <<EOF
X$decl
Xmain() { exit(0); }
Xt() { char *p = (char *) alloca(1); }
XEOF
Xif eval $compile; then
X :
Xelse
X alloca_missing=1
Xfi
Xrm -f conftest*
X
Xif test -n "$alloca_missing"; then
X # The SVR3 libPW and SVR4 libucb both contain incompatible functions
X # that cause trouble. Some versions do not even contain alloca or
X # contain a buggy version. If you still want to use their alloca,
X # use ar to extract alloca.o from them instead of compiling alloca.c.
X ALLOCA=alloca.o
Xfi
X
Xecho checking for BSD
X( test -f /vmunix || test -f /sdmach || test -f /../../mach ) && DEFS="$DEFS -DBSD42=1"
Xecho checking for HP-UX
Xtest -f /hp-ux && test ! -f /vmunix && MALLOC=malloc.o
X
Xecho checking for Xenix
Xcat > conftest.c <<EOF
X#if defined(M_XENIX) && !defined(M_UNIX)
X yes
X#endif
X
XEOF
Xeval "$CPP $DEFS conftest.c > conftest.out 2>&1"
Xif egrep "yes" conftest.out >/dev/null 2>&1; then
X XENIX=1
Xfi
Xrm -f conftest*
X
Xif test -n "$XENIX"; then
X DEFS="$DEFS -DVOID_CLOSEDIR=1"
X LIBS="$LIBS -lx"
X case "$DEFS" in
X *SYSNDIR*) ;;
X *) LIBS="-ldir $LIBS" ;; # Make sure -ldir precedes any -lx.
X esac
Xfi
X
Xlibname=`echo "socket" | sed 's/lib\([^\.]*\)\.a/\1/;s/-l//'`
XLIBS_save="${LIBS}"
XLIBS="${LIBS} -l${libname}"
Xhave_lib=""
Xecho checking for -l${libname}
Xcat > conftest.c <<EOF
X
Xmain() { exit(0); }
Xt() { main(); }
XEOF
Xif eval $compile; then
X have_lib="1"
Xfi
Xrm -f conftest*
XLIBS="${LIBS_save}"
Xif test -n "${have_lib}"; then
X :; LIBS="$LIBS -lsocket"
Xelse
X :;
Xfi
X
Xlibname=`echo "nsl" | sed 's/lib\([^\.]*\)\.a/\1/;s/-l//'`
XLIBS_save="${LIBS}"
XLIBS="${LIBS} -l${libname}"
Xhave_lib=""
Xecho checking for -l${libname}
Xcat > conftest.c <<EOF
X
Xmain() { exit(0); }
Xt() { main(); }
XEOF
Xif eval $compile; then
X have_lib="1"
Xfi
Xrm -f conftest*
XLIBS="${LIBS_save}"
Xif test -n "${have_lib}"; then
X :; LIBS="$LIBS -lnsl"
Xelse
X :;
Xfi
X
Xif test -n "$prefix"; then
X test -z "$exec_prefix" && exec_prefix='${prefix}'
X prsub="s%^prefix\\([ ]*\\)=\\([ ]*\\).*$%prefix\\1=\\2$prefix%"
Xfi
Xif test -n "$exec_prefix"; then
X prsub="$prsub
Xs%^exec_prefix\\([ ]*\\)=\\([ ]*\\).*$%\
Xexec_prefix\\1=\\2$exec_prefix%"
Xfi
X
Xtrap 'rm -f config.status; exit 1' 1 3 15
Xecho creating config.status
Xrm -f config.status
Xcat > config.status <<EOF
X#!/bin/sh
X# Generated automatically by configure.
X# Run this file to recreate the current configuration.
X# This directory was configured as follows,
X# on host `(hostname || uname -n) 2>/dev/null`:
X#
X# $0 $*
X
Xfor arg
Xdo
X case "\$arg" in
X -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
X exec /bin/sh $0 $* ;;
X *) echo "Usage: config.status --recheck" 2>&1; exit 1 ;;
X esac
Xdone
X
Xtrap 'rm -f Makefile; exit 1' 1 3 15
XPROGS='$PROGS'
XCC='$CC'
XCPP='$CPP'
XINSTALL='$INSTALL'
XINSTALL_PROGRAM='$INSTALL_PROGRAM'
XINSTALL_DATA='$INSTALL_DATA'
XYACC='$YACC'
XRTAPELIB='$RTAPELIB'
XDEF_AR_FILE='$DEF_AR_FILE'
XALLOCA='$ALLOCA'
XMALLOC='$MALLOC'
XLIBS='$LIBS'
Xsrcdir='$srcdir'
XDEFS='$DEFS'
Xprefix='$prefix'
Xexec_prefix='$exec_prefix'
Xprsub='$prsub'
XEOF
Xcat >> config.status <<\EOF
X
Xtop_srcdir=$srcdir
Xfor file in .. Makefile; do if [ "x$file" != "x.." ]; then
X srcdir=$top_srcdir
X # Remove last slash and all that follows it. Not all systems have dirname.
X dir=`echo $file|sed 's%/[^/][^/]*$%%'`
X if test "$dir" != "$file"; then
X test "$top_srcdir" != . && srcdir=$top_srcdir/$dir
X test ! -d $dir && mkdir $dir
X fi
X echo creating $file
X rm -f $file
X echo "# Generated automatically from `echo $file|sed 's|.*/||'`.in by configure." > $file
X sed -e "
X$prsub
Xs%@PROGS@%$PROGS%g
Xs%@CC@%$CC%g
Xs%@CPP@%$CPP%g
Xs%@INSTALL@%$INSTALL%g
Xs%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
Xs%@INSTALL_DATA@%$INSTALL_DATA%g
Xs%@YACC@%$YACC%g
Xs%@RTAPELIB@%$RTAPELIB%g
Xs%@DEF_AR_FILE@%$DEF_AR_FILE%g
Xs%@ALLOCA@%$ALLOCA%g
Xs%@MALLOC@%$MALLOC%g
Xs%@LIBS@%$LIBS%g
Xs%@srcdir@%$srcdir%g
Xs%@DEFS@%$DEFS%
X" $top_srcdir/${file}.in >> $file
Xfi; done
X
Xexit 0
XEOF
Xchmod +x config.status
Xtest -n "$no_create" || ./config.status
X
END_OF_FILE
if test 19657 -ne `wc -c <'configure'`; then
echo shar: \"'configure'\" unpacked with wrong size!
fi
chmod +x 'configure'
# end of 'configure'
fi
if test -f 'configure.in' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'configure.in'\"
else
echo shar: Extracting \"'configure.in'\" \(1551 characters\)
sed "s/^X//" >'configure.in' <<'END_OF_FILE'
Xdnl Process this file with autoconf to produce a configure script.
XAC_INIT(tar.h)
XPROGS="tar"
XAC_SUBST(PROGS)dnl
XAC_PROG_CC
XAC_PROG_CPP
XAC_GCC_TRADITIONAL
XAC_PROG_INSTALL
XAC_PROG_YACC
XAC_AIX
XAC_MINIX
XAC_ISC_POSIX
XAC_RETSIGTYPE
XAC_SIZE_T
XAC_MAJOR_HEADER
XAC_DIR_HEADER
X# The 3-argument open happens to go along with the O_* defines,
X# which are easier to check for.
XAC_HEADER_CHECK(fcntl.h, open_header=fcntl.h, open_header=sys/file.h)
XAC_COMPILE_CHECK(3-argument open,
X[#include <$open_header>], [int x = O_RDONLY;], , AC_DEFINE(EMUL_OPEN3))
XAC_REMOTE_TAPE
XAC_RSH
XAC_STDC_HEADERS
XAC_UNISTD_H
Xecho checking for getgrgid declaration
XAC_HEADER_EGREP(getgrgid, grp.h, AC_DEFINE(HAVE_GETGRGID))
Xecho checking for getpwuid declaration
XAC_HEADER_EGREP(getpwuid, pwd.h, AC_DEFINE(HAVE_GETPWUID))
XAC_HAVE_HEADERS(string.h limits.h)
Xecho checking default archive
X# This might guess wrong, but it's not very important.
Xfor dev in rmt8 rmt0 rmt0h rct0 rst0 tape rct/c7d0s2
Xdo
X if test -n "`ls /dev/$dev 2>/dev/null`"; then
X DEF_AR_FILE=/dev/$dev
X break
X fi
Xdone
Xif test -z "$DEF_AR_FILE"; then
X DEF_AR_FILE=-
Xfi
X
XAC_SUBST(DEF_AR_FILE)dnl
XAC_HAVE_FUNCS(strstr valloc mkdir mknod rename ftruncate ftime getcwd)
XAC_VPRINTF
XAC_ALLOCA
Xecho checking for BSD
X( test -f /vmunix || test -f /sdmach || test -f /../../mach ) && AC_DEFINE(BSD42)
Xecho checking for HP-UX
Xtest -f /hp-ux && test ! -f /vmunix && MALLOC=malloc.o
XAC_SUBST(MALLOC)
XAC_XENIX_DIR
XAC_HAVE_LIBRARY(socket, [LIBS="$LIBS -lsocket"])
XAC_HAVE_LIBRARY(nsl, [LIBS="$LIBS -lnsl"])
XAC_OUTPUT(Makefile)
END_OF_FILE
if test 1551 -ne `wc -c <'configure.in'`; then
echo shar: \"'configure.in'\" unpacked with wrong size!
fi
# end of 'configure.in'
fi
if test -f 'tar.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'tar.h'\"
else
echo shar: Extracting \"'tar.h'\" \(9318 characters\)
sed "s/^X//" >'tar.h' <<'END_OF_FILE'
X/* Declarations for tar archives.
X Copyright (C) 1988, 1992, 1993 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#include "testpad.h"
X
X/* major() and minor() macros (among other things) defined here for hpux */
X#ifdef hpux
X#include <sys/mknod.h>
X#endif
X
X/*
X * Kludge for handling systems that can't cope with multiple
X * external definitions of a variable. In ONE routine (tar.c),
X * we #define TAR_EXTERN to null; here, we set it to "extern" if
X * it is not already set.
X */
X#ifndef TAR_EXTERN
X#define TAR_EXTERN extern
X#endif
X
X/*
X * Header block on tape.
X *
X * I'm going to use traditional DP naming conventions here.
X * A "block" is a big chunk of stuff that we do I/O on.
X * A "record" is a piece of info that we care about.
X * Typically many "record"s fit into a "block".
X */
X#define RECORDSIZE 512
X#define NAMSIZ 100
X#define TUNMLEN 32
X#define TGNMLEN 32
X#define SPARSE_EXT_HDR 21
X#define SPARSE_IN_HDR 4
X
Xstruct sparse
X {
X char offset[12];
X char numbytes[12];
X };
X
Xstruct sp_array
X {
X int offset;
X int numbytes;
X };
X
Xunion record
X {
X char charptr[RECORDSIZE];
X struct header
X {
X char arch_name[NAMSIZ];
X char mode[8];
X char uid[8];
X char gid[8];
X char size[12];
X char mtime[12];
X char chksum[8];
X char linkflag;
X char arch_linkname[NAMSIZ];
X char magic[8];
X char uname[TUNMLEN];
X char gname[TGNMLEN];
X char devmajor[8];
X char devminor[8];
X /* these following fields were added by JF for gnu */
X /* and are NOT standard */
X char atime[12];
X char ctime[12];
X char offset[12];
X char longnames[4];
X#ifdef NEEDPAD
X char pad;
X#endif
X struct sparse sp[SPARSE_IN_HDR];
X char isextended;
X char realsize[12]; /* true size of the sparse file */
X /* char ending_blanks[12];*//* number of nulls at the
X end of the file, if any */
X }
X header;
X struct extended_header
X {
X struct sparse sp[21];
X char isextended;
X }
X ext_hdr;
X };
X
X/* The checksum field is filled with this while the checksum is computed. */
X#define CHKBLANKS " " /* 8 blanks, no null */
X
X/* The magic field is filled with this if uname and gname are valid. */
X#define TMAGIC "ustar " /* 7 chars and a null */
X
X/* The linkflag defines the type of file */
X#define LF_OLDNORMAL '\0' /* Normal disk file, Unix compat */
X#define LF_NORMAL '0' /* Normal disk file */
X#define LF_LINK '1' /* Link to previously dumped file */
X#define LF_SYMLINK '2' /* Symbolic link */
X#define LF_CHR '3' /* Character special file */
X#define LF_BLK '4' /* Block special file */
X#define LF_DIR '5' /* Directory */
X#define LF_FIFO '6' /* FIFO special file */
X#define LF_CONTIG '7' /* Contiguous file */
X/* Further link types may be defined later. */
X
X/* Note that the standards committee allows only capital A through
X capital Z for user-defined expansion. This means that defining something
X as, say '8' is a *bad* idea. */
X#define LF_DUMPDIR 'D' /* This is a dir entry that contains
X the names of files that were in
X the dir at the time the dump
X was made */
X#define LF_LONGLINK 'K' /* Identifies the NEXT file on the tape
X as having a long linkname */
X#define LF_LONGNAME 'L' /* Identifies the NEXT file on the tape
X as having a long name. */
X#define LF_MULTIVOL 'M' /* This is the continuation
X of a file that began on another
X volume */
X#define LF_NAMES 'N' /* For storing filenames that didn't
X fit in 100 characters */
X#define LF_SPARSE 'S' /* This is for sparse files */
X#define LF_VOLHDR 'V' /* This file is a tape/volume header */
X/* Ignore it on extraction */
X
X/*
X * Exit codes from the "tar" program
X */
X#define EX_SUCCESS 0 /* success! */
X#define EX_ARGSBAD 1 /* invalid args */
X#define EX_BADFILE 2 /* invalid filename */
X#define EX_BADARCH 3 /* bad archive */
X#define EX_SYSTEM 4 /* system gave unexpected error */
X#define EX_BADVOL 5 /* Special error code means
X Tape volume doesn't match the one
X specified on the command line */
X
X/*
X * Global variables
X */
XTAR_EXTERN union record *ar_block; /* Start of block of archive */
XTAR_EXTERN union record *ar_record; /* Current record of archive */
XTAR_EXTERN union record *ar_last; /* Last+1 record of archive block */
XTAR_EXTERN char ar_reading; /* 0 writing, !0 reading archive */
XTAR_EXTERN int blocking; /* Size of each block, in records */
XTAR_EXTERN int blocksize; /* Size of each block, in bytes */
XTAR_EXTERN char *info_script; /* Script to run at end of each tape change */
XTAR_EXTERN char *name_file; /* File containing names to work on */
XTAR_EXTERN char filename_terminator; /* \n or \0. */
XTAR_EXTERN char *tar; /* Name of this program */
XTAR_EXTERN struct sp_array *sparsearray; /* Pointer to the start of the scratch space */
XTAR_EXTERN int sp_array_size; /* Initial size of the sparsearray */
XTAR_EXTERN int tot_written; /* Total written to output */
XTAR_EXTERN struct re_pattern_buffer
X *label_pattern; /* compiled regex for extract label */
XTAR_EXTERN char **ar_files; /* list of tape drive names */
XTAR_EXTERN int n_ar_files; /* number of tape drive names */
XTAR_EXTERN int cur_ar_file; /* tape drive currently being used */
XTAR_EXTERN int ar_files_len; /* malloced size of ar_files */
XTAR_EXTERN char *current_file_name, *current_link_name;
X
X/*
X * Flags from the command line
X */
XTAR_EXTERN int cmd_mode;
X#define CMD_NONE 0
X#define CMD_CAT 1 /* -A */
X#define CMD_CREATE 2 /* -c */
X#define CMD_DIFF 3 /* -d */
X#define CMD_APPEND 4 /* -r */
X#define CMD_LIST 5 /* -t */
X#define CMD_UPDATE 6 /* -u */
X#define CMD_EXTRACT 7 /* -x */
X#define CMD_DELETE 8 /* -D */
X#define CMD_VERSION 9 /* --version */
X
X
XTAR_EXTERN int f_reblock; /* -B */
X#if 0
XTAR_EXTERN char f_dironly; /* -D */
X#endif
XTAR_EXTERN int f_run_script_at_end; /* -F */
XTAR_EXTERN int f_gnudump; /* -G */
XTAR_EXTERN int f_follow_links; /* -h */
XTAR_EXTERN int f_ignorez; /* -i */
XTAR_EXTERN int f_keep; /* -k */
XTAR_EXTERN int f_startfile; /* -K */
XTAR_EXTERN int f_local_filesys; /* -l */
XTAR_EXTERN int tape_length; /* -L */
XTAR_EXTERN int f_modified; /* -m */
XTAR_EXTERN int f_multivol; /* -M */
XTAR_EXTERN int f_new_files; /* -N */
XTAR_EXTERN int f_oldarch; /* -o */
XTAR_EXTERN int f_exstdout; /* -O */
XTAR_EXTERN int f_use_protection;/* -p */
XTAR_EXTERN int f_absolute_paths;/* -P */
XTAR_EXTERN int f_sayblock; /* -R */
XTAR_EXTERN int f_sorted_names; /* -s */
XTAR_EXTERN int f_sparse_files; /* -S ... JK */
XTAR_EXTERN int f_namefile; /* -T */
XTAR_EXTERN int f_verbose; /* -v */
XTAR_EXTERN char *f_volhdr; /* -V */
XTAR_EXTERN int f_confirm; /* -w */
XTAR_EXTERN int f_verify; /* -W */
XTAR_EXTERN int f_exclude; /* -X */
XTAR_EXTERN char *f_compressprog; /* -z and -Z */
XTAR_EXTERN int f_do_chown; /* --do-chown */
XTAR_EXTERN int f_totals; /* --totals */
XTAR_EXTERN int f_remove_files; /* --remove-files */
XTAR_EXTERN int f_ignore_failed_read; /* --ignore-failed-read */
XTAR_EXTERN int f_checkpoint; /* --checkpoint */
XTAR_EXTERN int f_show_omitted_dirs; /* --show-omitted-dirs */
XTAR_EXTERN char *f_volno_file; /* --volno-file */
XTAR_EXTERN int f_force_local; /* --force-local */
XTAR_EXTERN int f_atime_preserve;/* --atime-preserve */
XTAR_EXTERN int f_compress_block; /* --compress-block */
X
X/*
X * We default to Unix Standard format rather than 4.2BSD tar format.
X * The code can actually produce all three:
X * f_standard ANSI standard
X * f_oldarch V7
X * neither 4.2BSD
X * but we don't bother, since 4.2BSD can read ANSI standard format anyway.
X * The only advantage to the "neither" option is that we can cmp our
X * output to the output of 4.2BSD tar, for debugging.
X */
X#define f_standard (!f_oldarch)
X
X/*
X * Structure for keeping track of filenames and lists thereof.
X */
Xstruct name
X {
X struct name *next;
X short length; /* cached strlen(name) */
X char found; /* A matching file has been found */
X char firstch; /* First char is literally matched */
X char regexp; /* This name is a regexp, not literal */
X char *change_dir; /* JF set with the -C option */
X char *dir_contents; /* JF for f_gnudump */
X char fake; /* dummy entry */
X char name[1];
X };
X
XTAR_EXTERN struct name *namelist; /* Points to first name in list */
XTAR_EXTERN struct name *namelast; /* Points to last name in list */
X
XTAR_EXTERN int archive; /* File descriptor for archive file */
XTAR_EXTERN int errors; /* # of files in error */
X
XTAR_EXTERN char *gnu_dumpfile;
X
X/*
X * Error recovery stuff
X */
XTAR_EXTERN char read_error_flag;
X
X
X/*
X * Declarations of functions available to the world.
X */
Xunion record *findrec ();
Xvoid userec ();
Xunion record *endofrecs ();
Xvoid anno ();
X
X#if defined (HAVE_VPRINTF) && __STDC__
Xvoid msg (char *,...);
Xvoid msg_perror (char *,...);
X#else
Xvoid msg ();
Xvoid msg_perror ();
X#endif
END_OF_FILE
if test 9318 -ne `wc -c <'tar.h'`; then
echo shar: \"'tar.h'\" unpacked with wrong size!
fi
# end of 'tar.h'
fi
if test -f 'fnmatch.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'fnmatch.h'\"
else
echo shar: Extracting \"'fnmatch.h'\" \(1955 characters\)
sed "s/^X//" >'fnmatch.h' <<'END_OF_FILE'
X/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
X
XThis library is free software; you can redistribute it and/or
Xmodify it under the terms of the GNU Library General Public License as
Xpublished by the Free Software Foundation; either version 2 of the
XLicense, or (at your option) any later version.
X
XThis library is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
XLibrary General Public License for more details.
X
XYou should have received a copy of the GNU Library General Public
XLicense along with this library; see the file COPYING.LIB. If
Xnot, write to the Free Software Foundation, Inc., 675 Mass Ave,
XCambridge, MA 02139, USA. */
X
X#ifndef _FNMATCH_H
X
X#define _FNMATCH_H 1
X
X#ifdef __cplusplus
Xextern "C" {
X#endif
X
X#if defined (__cplusplus) || (defined (__STDC__) && __STDC__)
X#undef __P
X#define __P(args) args
X#else /* Not C++ or ANSI C. */
X#undef __P
X#define __P(args) ()
X#undef const
X#define const
X#endif /* C++ or ANSI C. */
X
X/* Bits set in the FLAGS argument to `fnmatch'. */
X#define FNM_PATHNAME (1 << 0)/* No wildcard can ever match `/'. */
X#define FNM_NOESCAPE (1 << 1)/* Backslashes don't quote special chars. */
X#define FNM_PERIOD (1 << 2)/* Leading `.' is matched only explicitly. */
X#define __FNM_FLAGS (FNM_PATHNAME|FNM_NOESCAPE|FNM_PERIOD|FNM_LEADING_DIR)
X
X#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_BSD_SOURCE)
X#define FNM_LEADING_DIR (1 << 3)/* Ignore `/...' after a match. */
X#define FNM_FILE_NAME FNM_PATHNAME
X#endif
X
X/* Value returned by `fnmatch' if STRING does not match PATTERN. */
X#define FNM_NOMATCH 1
X
X/* Match STRING against the filename pattern PATTERN,
X returning zero if it matches, FNM_NOMATCH if not. */
Xextern int fnmatch __P ((const char *__pattern, const char *__string,
X int __flags));
X
X#ifdef __cplusplus
X}
X#endif
X
X#endif /* fnmatch.h */
END_OF_FILE
if test 1955 -ne `wc -c <'fnmatch.h'`; then
echo shar: \"'fnmatch.h'\" unpacked with wrong size!
fi
# end of 'fnmatch.h'
fi
if test -f 'pathmax.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'pathmax.h'\"
else
echo shar: Extracting \"'pathmax.h'\" \(1691 characters\)
sed "s/^X//" >'pathmax.h' <<'END_OF_FILE'
X/* Define PATH_MAX somehow. Requires sys/types.h.
X Copyright (C) 1992 Free Software Foundation, Inc.
X
X This program is free software; you can redistribute it and/or modify
X it under the terms of the GNU General Public License as published by
X the Free Software Foundation; either version 2, or (at your option)
X any later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#ifndef _PATHMAX_H
X#define _PATHMAX_H
X
X#ifdef HAVE_UNISTD_H
X#include <unistd.h>
X#endif
X
X/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
X PATH_MAX but might cause redefinition warnings when sys/param.h is
X later included (as on MORE/BSD 4.3). */
X#if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && defined(USG))
X#include <limits.h>
X#endif
X
X#ifndef _POSIX_PATH_MAX
X#define _POSIX_PATH_MAX 255
X#endif
X
X#if !defined(PATH_MAX) && defined(_PC_PATH_MAX)
X#define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
X#endif
X
X/* Don't include sys/param.h if it already has been. */
X#if !defined(PATH_MAX) && !defined(MAXPATHLEN) && !defined(__MSDOS__)
X#include <sys/param.h>
X#endif
X
X#if !defined(PATH_MAX) && defined(MAXPATHLEN)
X#define PATH_MAX MAXPATHLEN
X#endif
X
X#ifndef PATH_MAX
X#define PATH_MAX _POSIX_PATH_MAX
X#endif
X
X#endif /* _PATHMAX_H */
END_OF_FILE
if test 1691 -ne `wc -c <'pathmax.h'`; then
echo shar: \"'pathmax.h'\" unpacked with wrong size!
fi
# end of 'pathmax.h'
fi
if test -f 'port.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'port.h'\"
else
echo shar: Extracting \"'port.h'\" \(5312 characters\)
sed "s/^X//" >'port.h' <<'END_OF_FILE'
X/* Portability declarations. Requires sys/types.h.
X Copyright (C) 1988, 1992 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* AIX requires this to be the first thing in the file. */
X#ifdef __GNUC__
X#define alloca __builtin_alloca
X#else /* not __GNUC__ */
X#if HAVE_ALLOCA_H
X#include <alloca.h>
X#else /* not HAVE_ALLOCA_H */
X#ifdef _AIX
X #pragma alloca
X#else /* not _AIX */
Xchar *alloca ();
X#endif /* not _AIX */
X#endif /* not HAVE_ALLOCA_H */
X#endif /* not __GNUC__ */
X
X#include "pathmax.h"
X
X#ifdef _POSIX_VERSION
X#include <sys/wait.h>
X#else /* !_POSIX_VERSION */
X#define WIFSTOPPED(w) (((w) & 0xff) == 0x7f)
X#define WIFSIGNALED(w) (((w) & 0xff) != 0x7f && ((w) & 0xff) != 0)
X#define WIFEXITED(w) (((w) & 0xff) == 0)
X
X#define WSTOPSIG(w) (((w) >> 8) & 0xff)
X#define WTERMSIG(w) ((w) & 0x7f)
X#define WEXITSTATUS(w) (((w) >> 8) & 0xff)
X#endif /* _POSIX_VERSION */
X
X/* nonstandard */
X#ifndef WIFCOREDUMPED
X#define WIFCOREDUMPED(w) (((w) & 0x80) != 0)
X#endif
X
X#ifdef __MSDOS__
X/* missing things from sys/stat.h */
X#define S_ISUID 0
X#define S_ISGID 0
X#define S_ISVTX 0
X
X/* device stuff */
X#define makedev(ma, mi) ((ma << 8) | mi)
X#define major(dev) (dev)
X#define minor(dev) (dev)
Xtypedef long off_t;
X#endif /* __MSDOS__ */
X
X#if defined(__STDC__) || defined(__TURBOC__)
X#define PTR void *
X#else
X#define PTR char *
X#define const
X#endif
X
X/* Since major is a function on SVR4, we can't just use `ifndef major'. */
X#ifdef major /* Might be defined in sys/types.h. */
X#define HAVE_MAJOR
X#endif
X
X#if !defined(HAVE_MAJOR) && defined(MAJOR_IN_MKDEV)
X#include <sys/mkdev.h>
X#define HAVE_MAJOR
X#endif
X
X#if !defined(HAVE_MAJOR) && defined(MAJOR_IN_SYSMACROS)
X#include <sys/sysmacros.h>
X#define HAVE_MAJOR
X#endif
X
X#ifndef HAVE_MAJOR
X#define major(dev) (((dev) >> 8) & 0xff)
X#define minor(dev) ((dev) & 0xff)
X#define makedev(maj, min) (((maj) << 8) | (min))
X#endif
X#undef HAVE_MAJOR
X
X#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
X#include <string.h>
X#if !defined(__MSDOS__) && !defined(STDC_HEADERS)
X#include <memory.h>
X#endif
X#ifdef index
X#undef index
X#endif
X#ifdef rindex
X#undef rindex
X#endif
X#define index strchr
X#define rindex strrchr
X#define bcopy(s, d, n) memcpy(d, s, n)
X#define bzero(s, n) memset(s, 0, n)
X#define bcmp memcmp
X#else
X#include <strings.h>
X#endif
X
X#if defined(STDC_HEADERS)
X#include <stdlib.h>
X#else
Xchar *malloc (), *realloc ();
Xchar *getenv ();
X#endif
X
X#ifndef _POSIX_VERSION
X#ifdef __MSDOS__
X#include <io.h>
X#else /* !__MSDOS__ */
Xoff_t lseek ();
X#endif /* !__MSDOS__ */
Xchar *getcwd ();
X#endif /* !_POSIX_VERSION */
X
X#ifndef NULL
X#define NULL 0
X#endif
X
X#ifndef O_BINARY
X#define O_BINARY 0
X#endif
X#ifndef O_CREAT
X#define O_CREAT 0
X#endif
X#ifndef O_NDELAY
X#define O_NDELAY 0
X#endif
X#ifndef O_RDONLY
X#define O_RDONLY 0
X#endif
X#ifndef O_RDWR
X#define O_RDWR 2
X#endif
X
X#include <sys/stat.h>
X#ifndef S_ISREG /* Doesn't have POSIX.1 stat stuff. */
X#define mode_t unsigned short
X#endif
X#if !defined(S_ISBLK) && defined(S_IFBLK)
X#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
X#endif
X#if !defined(S_ISCHR) && defined(S_IFCHR)
X#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
X#endif
X#if !defined(S_ISDIR) && defined(S_IFDIR)
X#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
X#endif
X#if !defined(S_ISREG) && defined(S_IFREG)
X#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
X#endif
X#if !defined(S_ISFIFO) && defined(S_IFIFO)
X#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
X#define mkfifo(path, mode) (mknod ((path), (mode) | S_IFIFO, 0))
X#endif
X#if !defined(S_ISLNK) && defined(S_IFLNK)
X#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
X#endif
X#if !defined(S_ISSOCK) && defined(S_IFSOCK)
X#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
X#endif
X#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
X#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
X#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
X#endif
X#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
X#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
X#endif
X#if !defined(S_ISCTG) && defined(S_IFCTG) /* contiguous file */
X#define S_ISCTG(m) (((m) & S_IFMT) == S_IFCTG)
X#endif
X#if !defined(S_ISVTX)
X#define S_ISVTX 0001000
X#endif
X
X#ifdef __MSDOS__
X#include "msd_dir.h"
X#define NLENGTH(direct) ((direct)->d_namlen)
X
X#else /* not __MSDOS__ */
X
X#if defined(DIRENT) || defined(_POSIX_VERSION)
X#include <dirent.h>
X#define NLENGTH(direct) (strlen((direct)->d_name))
X#else /* not (DIRENT or _POSIX_VERSION) */
X#define dirent direct
X#define NLENGTH(direct) ((direct)->d_namlen)
X#ifdef SYSNDIR
X#include <sys/ndir.h>
X#endif /* SYSNDIR */
X#ifdef SYSDIR
X#include <sys/dir.h>
X#endif /* SYSDIR */
X#ifdef NDIR
X#include <ndir.h>
X#endif /* NDIR */
X#endif /* DIRENT or _POSIX_VERSION */
X
X#endif /* not __MSDOS__ */
END_OF_FILE
if test 5312 -ne `wc -c <'port.h'`; then
echo shar: \"'port.h'\" unpacked with wrong size!
fi
# end of 'port.h'
fi
if test -f 'open3.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'open3.h'\"
else
echo shar: Extracting \"'open3.h'\" \(2639 characters\)
sed "s/^X//" >'open3.h' <<'END_OF_FILE'
X/* Defines for Sys V style 3-argument open call.
X Copyright (C) 1988 Free Software Foundation
X
XThis file is part of GNU Tar.
X
XGNU Tar is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 2, or (at your option)
Xany later version.
X
XGNU Tar is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU Tar; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/*
X * open3.h -- #defines for the various flags for the Sys V style 3-argument
X * open() call. On BSD or System 5, the system already has this in an
X * include file. This file is needed for V7 and MINIX systems for the
X * benefit of open3() in port.c, a routine that emulates the 3-argument
X * call using system calls available on V7/MINIX.
X *
X * This file is needed by PD tar even if we aren't using the
X * emulator, since the #defines for O_WRONLY, etc. are used in
X * a couple of places besides the open() calls, (e.g. in the assignment
X * to openflag in extract.c). We just #include this rather than
X * #ifdef them out.
X *
X * Written 6/10/87 by rmtodd@uokmax (Richard Todd).
X *
X * The names have been changed by John Gilmore, 31 July 1987, since
X * Richard called it "bsdopen", and really this change was introduced in
X * AT&T Unix systems before BSD picked it up.
X */
X
X/* Only one of the next three should be specified */
X#define O_RDONLY 0 /* only allow read */
X#define O_WRONLY 1 /* only allow write */
X#define O_RDWR 2 /* both are allowed */
X
X/* The rest of these can be OR-ed in to the above. */
X/*
X * O_NDELAY isn't implemented by the emulator. It's only useful (to tar) on
X * systems that have named pipes anyway; it prevents tar's hanging by
X * opening a named pipe. We #ifndef it because some systems already have
X * it defined.
X */
X#ifndef O_NDELAY
X#define O_NDELAY 4 /* don't block on opening devices that would
X * block on open -- ignored by emulator. */
X#endif
X#define O_CREAT 8 /* create file if needed */
X#define O_EXCL 16 /* file cannot already exist */
X#define O_TRUNC 32 /* truncate file on open */
X#define O_APPEND 64 /* always write at end of file -- ignored by emul */
X
X#ifdef EMUL_OPEN3
X/*
X * make emulation transparent to rest of file -- redirect all open() calls
X * to our routine
X */
X#define open open3
X#endif
END_OF_FILE
if test 2639 -ne `wc -c <'open3.h'`; then
echo shar: \"'open3.h'\" unpacked with wrong size!
fi
# end of 'open3.h'
fi
if test -f 'getopt.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'getopt.h'\"
else
echo shar: Extracting \"'getopt.h'\" \(4333 characters\)
sed "s/^X//" >'getopt.h' <<'END_OF_FILE'
X/* Declarations for getopt.
X Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
X
X This program is free software; you can redistribute it and/or modify it
X under the terms of the GNU General Public License as published by the
X Free Software Foundation; either version 2, or (at your option) any
X later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#ifndef _GETOPT_H
X#define _GETOPT_H 1
X
X#ifdef __cplusplus
Xextern "C" {
X#endif
X
X/* For communication from `getopt' to the caller.
X When `getopt' finds an option that takes an argument,
X the argument value is returned here.
X Also, when `ordering' is RETURN_IN_ORDER,
X each non-option ARGV-element is returned here. */
X
Xextern char *optarg;
X
X/* Index in ARGV of the next element to be scanned.
X This is used for communication to and from the caller
X and for communication between successive calls to `getopt'.
X
X On entry to `getopt', zero means this is the first call; initialize.
X
X When `getopt' returns EOF, this is the index of the first of the
X non-option elements that the caller should itself scan.
X
X Otherwise, `optind' communicates from one call to the next
X how much of ARGV has been scanned so far. */
X
Xextern int optind;
X
X/* Callers store zero here to inhibit the error message `getopt' prints
X for unrecognized options. */
X
Xextern int opterr;
X
X/* Describe the long-named options requested by the application.
X The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
X of `struct option' terminated by an element containing a name which is
X zero.
X
X The field `has_arg' is:
X no_argument (or 0) if the option does not take an argument,
X required_argument (or 1) if the option requires an argument,
X optional_argument (or 2) if the option takes an optional argument.
X
X If the field `flag' is not NULL, it points to a variable that is set
X to the value given in the field `val' when the option is found, but
X left unchanged if the option is not found.
X
X To have a long-named option do something other than set an `int' to
X a compiled-in constant, such as set a value from `optarg', set the
X option's `flag' field to zero and its `val' field to a nonzero
X value (the equivalent single-letter option character, if there is
X one). For long options that have a zero `flag' field, `getopt'
X returns the contents of the `val' field. */
X
Xstruct option
X{
X#if __STDC__
X const char *name;
X#else
X char *name;
X#endif
X /* has_arg can't be an enum because some compilers complain about
X type mismatches in all the code that assumes it is an int. */
X int has_arg;
X int *flag;
X int val;
X};
X
X/* Names for the values of the `has_arg' field of `struct option'. */
X
X#define no_argument 0
X#define required_argument 1
X#define optional_argument 2
X
X#if __STDC__
X#if defined(__GNU_LIBRARY__)
X/* Many other libraries have conflicting prototypes for getopt, with
X differences in the consts, in stdlib.h. To avoid compilation
X errors, only prototype getopt for the GNU C library. */
Xextern int getopt (int argc, char *const *argv, const char *shortopts);
X#else /* not __GNU_LIBRARY__ */
Xextern int getopt ();
X#endif /* not __GNU_LIBRARY__ */
Xextern int getopt_long (int argc, char *const *argv, const char *shortopts,
X const struct option *longopts, int *longind);
Xextern int getopt_long_only (int argc, char *const *argv,
X const char *shortopts,
X const struct option *longopts, int *longind);
X
X/* Internal only. Users should not call this directly. */
Xextern int _getopt_internal (int argc, char *const *argv,
X const char *shortopts,
X const struct option *longopts, int *longind,
X int long_only);
X#else /* not __STDC__ */
Xextern int getopt ();
Xextern int getopt_long ();
Xextern int getopt_long_only ();
X
Xextern int _getopt_internal ();
X#endif /* not __STDC__ */
X
X#ifdef __cplusplus
X}
X#endif
X
X#endif /* _GETOPT_H */
END_OF_FILE
if test 4333 -ne `wc -c <'getopt.h'`; then
echo shar: \"'getopt.h'\" unpacked with wrong size!
fi
# end of 'getopt.h'
fi
if test -f 'regex.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'regex.h'\"
else
echo shar: Extracting \"'regex.h'\" \(18688 characters\)
sed "s/^X//" >'regex.h' <<'END_OF_FILE'
X/* Definitions for data structures and routines for the regular
X expression library, version 0.11.
X
X Copyright (C) 1985, 89, 90, 91, 92 Free Software Foundation, Inc.
X
X This program is free software; you can redistribute it and/or modify
X it under the terms of the GNU General Public License as published by
X the Free Software Foundation; either version 2, or (at your option)
X any later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#ifndef __REGEXP_LIBRARY_H__
X#define __REGEXP_LIBRARY_H__
X
X/* POSIX says that <sys/types.h> must be included (by the caller) before
X <regex.h>. */
X
X#ifdef VMS
X/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
X should be there. */
X#include <stddef.h>
X#endif
X
X
X/* The following bits are used to determine the regexp syntax we
X recognize. The set/not-set meanings are chosen so that Emacs syntax
X remains the value 0. The bits are given in alphabetical order, and
X the definitions shifted by one from the previous bit; thus, when we
X add or remove a bit, only one other definition need change. */
Xtypedef unsigned reg_syntax_t;
X
X/* If this bit is not set, then \ inside a bracket expression is literal.
X If set, then such a \ quotes the following character. */
X#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
X
X/* If this bit is not set, then + and ? are operators, and \+ and \? are
X literals.
X If set, then \+ and \? are operators and + and ? are literals. */
X#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
X
X/* If this bit is set, then character classes are supported. They are:
X [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
X [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
X If not set, then character classes are not supported. */
X#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
X
X/* If this bit is set, then ^ and $ are always anchors (outside bracket
X expressions, of course).
X If this bit is not set, then it depends:
X ^ is an anchor if it is at the beginning of a regular
X expression or after an open-group or an alternation operator;
X $ is an anchor if it is at the end of a regular expression, or
X before a close-group or an alternation operator.
X
X This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
X POSIX draft 11.2 says that * etc. in leading positions is undefined.
X We already implemented a previous draft which made those constructs
X invalid, though, so we haven't changed the code back. */
X#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
X
X/* If this bit is set, then special characters are always special
X regardless of where they are in the pattern.
X If this bit is not set, then special characters are special only in
X some contexts; otherwise they are ordinary. Specifically,
X * + ? and intervals are only special when not after the beginning,
X open-group, or alternation operator. */
X#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
X
X/* If this bit is set, then *, +, ?, and { cannot be first in an re or
X immediately after an alternation or begin-group operator. */
X#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
X
X/* If this bit is set, then . matches newline.
X If not set, then it doesn't. */
X#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
X
X/* If this bit is set, then . doesn't match NUL.
X If not set, then it does. */
X#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
X
X/* If this bit is set, nonmatching lists [^...] do not match newline.
X If not set, they do. */
X#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
X
X/* If this bit is set, either \{...\} or {...} defines an
X interval, depending on RE_NO_BK_BRACES.
X If not set, \{, \}, {, and } are literals. */
X#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
X
X/* If this bit is set, +, ? and | aren't recognized as operators.
X If not set, they are. */
X#define RE_LIMITED_OPS (RE_INTERVALS << 1)
X
X/* If this bit is set, newline is an alternation operator.
X If not set, newline is literal. */
X#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
X
X/* If this bit is set, then `{...}' defines an interval, and \{ and \}
X are literals.
X If not set, then `\{...\}' defines an interval. */
X#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
X
X/* If this bit is set, (...) defines a group, and \( and \) are literals.
X If not set, \(...\) defines a group, and ( and ) are literals. */
X#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
X
X/* If this bit is set, then \<digit> matches <digit>.
X If not set, then \<digit> is a back-reference. */
X#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
X
X/* If this bit is set, then | is an alternation operator, and \| is literal.
X If not set, then \| is an alternation operator, and | is literal. */
X#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
X
X/* If this bit is set, then an ending range point collating higher
X than the starting range point, as in [z-a], is invalid.
X If not set, then when ending range point collates higher than the
X starting range point, the range is ignored. */
X#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
X
X/* If this bit is set, then an unmatched ) is ordinary.
X If not set, then an unmatched ) is invalid. */
X#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
X
X/* This global variable defines the particular regexp syntax to use (for
X some interfaces). When a regexp is compiled, the syntax used is
X stored in the pattern buffer, so changing this does not affect
X already-compiled regexps. */
Xextern reg_syntax_t re_syntax_options;
X
X/* Define combinations of the above bits for the standard possibilities.
X (The [[[ comments delimit what gets put into the Texinfo file, so
X don't delete them!) */
X/* [[[begin syntaxes]]] */
X#define RE_SYNTAX_EMACS 0
X
X#define RE_SYNTAX_AWK \
X (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
X | RE_NO_BK_PARENS | RE_NO_BK_REFS \
X | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
X | RE_UNMATCHED_RIGHT_PAREN_ORD)
X
X#define RE_SYNTAX_POSIX_AWK \
X (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
X
X#define RE_SYNTAX_GREP \
X (RE_BK_PLUS_QM | RE_CHAR_CLASSES \
X | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
X | RE_NEWLINE_ALT)
X
X#define RE_SYNTAX_EGREP \
X (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
X | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
X | RE_NEWLINE_ALT | RE_NO_BK_PARENS \
X | RE_NO_BK_VBAR)
X
X#define RE_SYNTAX_POSIX_EGREP \
X (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
X
X/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
X#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
X
X#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
X
X/* Syntax bits common to both basic and extended POSIX regex syntax. */
X#define _RE_SYNTAX_POSIX_COMMON \
X (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
X | RE_INTERVALS | RE_NO_EMPTY_RANGES)
X
X#define RE_SYNTAX_POSIX_BASIC \
X (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
X
X/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
X RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
X isn't minimal, since other operators, such as \`, aren't disabled. */
X#define RE_SYNTAX_POSIX_MINIMAL_BASIC \
X (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
X
X#define RE_SYNTAX_POSIX_EXTENDED \
X (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
X | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
X | RE_NO_BK_PARENS | RE_NO_BK_VBAR \
X | RE_UNMATCHED_RIGHT_PAREN_ORD)
X
X/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
X replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */
X#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
X (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
X | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
X | RE_NO_BK_PARENS | RE_NO_BK_REFS \
X | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
X/* [[[end syntaxes]]] */
X
X/* Maximum number of duplicates an interval can allow. Some systems
X (erroneously) define this in other header files, but we want our
X value, so remove any previous define. */
X#ifdef RE_DUP_MAX
X#undef RE_DUP_MAX
X#endif
X#define RE_DUP_MAX ((1 << 15) - 1)
X
X
X/* POSIX `cflags' bits (i.e., information for `regcomp'). */
X
X/* If this bit is set, then use extended regular expression syntax.
X If not set, then use basic regular expression syntax. */
X#define REG_EXTENDED 1
X
X/* If this bit is set, then ignore case when matching.
X If not set, then case is significant. */
X#define REG_ICASE (REG_EXTENDED << 1)
X
X/* If this bit is set, then anchors do not match at newline
X characters in the string.
X If not set, then anchors do match at newlines. */
X#define REG_NEWLINE (REG_ICASE << 1)
X
X/* If this bit is set, then report only success or fail in regexec.
X If not set, then returns differ between not matching and errors. */
X#define REG_NOSUB (REG_NEWLINE << 1)
X
X
X/* POSIX `eflags' bits (i.e., information for regexec). */
X
X/* If this bit is set, then the beginning-of-line operator doesn't match
X the beginning of the string (presumably because it's not the
X beginning of a line).
X If not set, then the beginning-of-line operator does match the
X beginning of the string. */
X#define REG_NOTBOL 1
X
X/* Like REG_NOTBOL, except for the end-of-line. */
X#define REG_NOTEOL (1 << 1)
X
X
X/* If any error codes are removed, changed, or added, update the
X `re_error_msg' table in regex.c. */
Xtypedef enum
X{
X REG_NOERROR = 0, /* Success. */
X REG_NOMATCH, /* Didn't find a match (for regexec). */
X
X /* POSIX regcomp return error codes. (In the order listed in the
X standard.) */
X REG_BADPAT, /* Invalid pattern. */
X REG_ECOLLATE, /* Not implemented. */
X REG_ECTYPE, /* Invalid character class name. */
X REG_EESCAPE, /* Trailing backslash. */
X REG_ESUBREG, /* Invalid back reference. */
X REG_EBRACK, /* Unmatched left bracket. */
X REG_EPAREN, /* Parenthesis imbalance. */
X REG_EBRACE, /* Unmatched \{. */
X REG_BADBR, /* Invalid contents of \{\}. */
X REG_ERANGE, /* Invalid range end. */
X REG_ESPACE, /* Ran out of memory. */
X REG_BADRPT, /* No preceding re for repetition op. */
X
X /* Error codes we've added. */
X REG_EEND, /* Premature end. */
X REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
X REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
X} reg_errcode_t;
X
X/* This data structure represents a compiled pattern. Before calling
X the pattern compiler, the fields `buffer', `allocated', `fastmap',
X `translate', and `no_sub' can be set. After the pattern has been
X compiled, the `re_nsub' field is available. All other fields are
X private to the regex routines. */
X
Xstruct re_pattern_buffer
X{
X/* [[[begin pattern_buffer]]] */
X /* Space that holds the compiled pattern. It is declared as
X `unsigned char *' because its elements are
X sometimes used as array indexes. */
X unsigned char *buffer;
X
X /* Number of bytes to which `buffer' points. */
X unsigned long allocated;
X
X /* Number of bytes actually used in `buffer'. */
X unsigned long used;
X
X /* Syntax setting with which the pattern was compiled. */
X reg_syntax_t syntax;
X
X /* Pointer to a fastmap, if any, otherwise zero. re_search uses
X the fastmap, if there is one, to skip over impossible
X starting points for matches. */
X char *fastmap;
X
X /* Either a translate table to apply to all characters before
X comparing them, or zero for no translation. The translation
X is applied to a pattern when it is compiled and to a string
X when it is matched. */
X char *translate;
X
X /* Number of subexpressions found by the compiler. */
X size_t re_nsub;
X
X /* Zero if this pattern cannot match the empty string, one else.
X Well, in truth it's used only in `re_search_2', to see
X whether or not we should use the fastmap, so we don't set
X this absolutely perfectly; see `re_compile_fastmap' (the
X `duplicate' case). */
X unsigned can_be_null : 1;
X
X /* If REGS_UNALLOCATED, allocate space in the `regs' structure
X for `max (RE_NREGS, re_nsub + 1)' groups.
X If REGS_REALLOCATE, reallocate space if necessary.
X If REGS_FIXED, use what's there. */
X#define REGS_UNALLOCATED 0
X#define REGS_REALLOCATE 1
X#define REGS_FIXED 2
X unsigned regs_allocated : 2;
X
X /* Set to zero when `regex_compile' compiles a pattern; set to one
X by `re_compile_fastmap' if it updates the fastmap. */
X unsigned fastmap_accurate : 1;
X
X /* If set, `re_match_2' does not return information about
X subexpressions. */
X unsigned no_sub : 1;
X
X /* If set, a beginning-of-line anchor doesn't match at the
X beginning of the string. */
X unsigned not_bol : 1;
X
X /* Similarly for an end-of-line anchor. */
X unsigned not_eol : 1;
X
X /* If true, an anchor at a newline matches. */
X unsigned newline_anchor : 1;
X
X/* [[[end pattern_buffer]]] */
X};
X
Xtypedef struct re_pattern_buffer regex_t;
X
X
X/* search.c (search_buffer) in Emacs needs this one opcode value. It is
X defined both in `regex.c' and here. */
X#define RE_EXACTN_VALUE 1
X
X/* Type for byte offsets within the string. POSIX mandates this. */
Xtypedef int regoff_t;
X
X
X/* This is the structure we store register match data in. See
X regex.texinfo for a full description of what registers match. */
Xstruct re_registers
X{
X unsigned num_regs;
X regoff_t *start;
X regoff_t *end;
X};
X
X
X/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
X `re_match_2' returns information about at least this many registers
X the first time a `regs' structure is passed. */
X#ifndef RE_NREGS
X#define RE_NREGS 30
X#endif
X
X
X/* POSIX specification for registers. Aside from the different names than
X `re_registers', POSIX uses an array of structures, instead of a
X structure of arrays. */
Xtypedef struct
X{
X regoff_t rm_so; /* Byte offset from string's start to substring's start. */
X regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
X} regmatch_t;
X
X/* Declarations for routines. */
X
X/* To avoid duplicating every routine declaration -- once with a
X prototype (if we are ANSI), and once without (if we aren't) -- we
X use the following macro to declare argument types. This
X unfortunately clutters up the declarations a bit, but I think it's
X worth it. */
X
X#if __STDC__
X
X#define _RE_ARGS(args) args
X
X#else /* not __STDC__ */
X
X#define _RE_ARGS(args) ()
X
X#endif /* not __STDC__ */
X
X/* Sets the current default syntax to SYNTAX, and return the old syntax.
X You can also simply assign to the `re_syntax_options' variable. */
Xextern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
X
X/* Compile the regular expression PATTERN, with length LENGTH
X and syntax given by the global `re_syntax_options', into the buffer
X BUFFER. Return NULL if successful, and an error string if not. */
Xextern const char *re_compile_pattern
X _RE_ARGS ((const char *pattern, int length,
X struct re_pattern_buffer *buffer));
X
X
X/* Compile a fastmap for the compiled pattern in BUFFER; used to
X accelerate searches. Return 0 if successful and -2 if was an
X internal error. */
Xextern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
X
X
X/* Search in the string STRING (with length LENGTH) for the pattern
X compiled into BUFFER. Start searching at position START, for RANGE
X characters. Return the starting position of the match, -1 for no
X match, or -2 for an internal error. Also return register
X information in REGS (if REGS and BUFFER->no_sub are nonzero). */
Xextern int re_search
X _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
X int length, int start, int range, struct re_registers *regs));
X
X
X/* Like `re_search', but search in the concatenation of STRING1 and
X STRING2. Also, stop searching at index START + STOP. */
Xextern int re_search_2
X _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
X int length1, const char *string2, int length2,
X int start, int range, struct re_registers *regs, int stop));
X
X
X/* Like `re_search', but return how many characters in STRING the regexp
X in BUFFER matched, starting at position START. */
Xextern int re_match
X _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
X int length, int start, struct re_registers *regs));
X
X
X/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
Xextern int re_match_2
X _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
X int length1, const char *string2, int length2,
X int start, struct re_registers *regs, int stop));
X
X
X/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
X ENDS. Subsequent matches using BUFFER and REGS will use this memory
X for recording register information. STARTS and ENDS must be
X allocated with malloc, and must each be at least `NUM_REGS * sizeof
X (regoff_t)' bytes long.
X
X If NUM_REGS == 0, then subsequent matches should allocate their own
X register data.
X
X Unless this function is called, the first search or match using
X PATTERN_BUFFER will allocate its own register data, without
X freeing the old data. */
Xextern void re_set_registers
X _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
X unsigned num_regs, regoff_t *starts, regoff_t *ends));
X
X/* 4.2 bsd compatibility. */
Xextern char *re_comp _RE_ARGS ((const char *));
Xextern int re_exec _RE_ARGS ((const char *));
X
X/* POSIX compatibility. */
Xextern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags));
Xextern int regexec
X _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch,
X regmatch_t pmatch[], int eflags));
Xextern size_t regerror
X _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf,
X size_t errbuf_size));
Xextern void regfree _RE_ARGS ((regex_t *preg));
X
X#endif /* not __REGEXP_LIBRARY_H__ */
X
X/*
XLocal variables:
Xmake-backup-files: t
Xversion-control: t
Xtrim-versions-without-asking: nil
XEnd:
X*/
END_OF_FILE
if test 18688 -ne `wc -c <'regex.h'`; then
echo shar: \"'regex.h'\" unpacked with wrong size!
fi
# end of 'regex.h'
fi
if test -f 'rmt.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'rmt.h'\"
else
echo shar: Extracting \"'rmt.h'\" \(3321 characters\)
sed "s/^X//" >'rmt.h' <<'END_OF_FILE'
X/* Definitions for communicating with a remote tape drive.
X Copyright (C) 1988, 1992 Free Software Foundation, Inc.
X
X This program is free software; you can redistribute it and/or modify
X it under the terms of the GNU General Public License as published by
X the Free Software Foundation; either version 2, or (at your option)
X any later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#ifdef HAVE_UNISTD_H
X#include <unistd.h>
X#endif
X
X#if !defined(_POSIX_VERSION)
X#ifdef __MSDOS__
X#include <io.h>
X#else /* !__MSDOS__ */
Xextern off_t lseek ();
X#endif /* __MSDOS__ */
X#endif /* _POSIX_VERSION */
X
X#ifdef NO_REMOTE
X#define _isrmt(f) 0
X#define rmtopen open
X#define rmtaccess access
X#define rmtstat stat
X#define rmtcreat creat
X#define rmtlstat lstat
X#define rmtread read
X#define rmtwrite write
X#define rmtlseek lseek
X#define rmtclose close
X#define rmtioctl ioctl
X#define rmtdup dup
X#define rmtfstat fstat
X#define rmtfcntl fcntl
X#define rmtisatty isatty
X
X#else /* !NO_REMOTE */
X
X#define __REM_BIAS 128
X#define RMTIOCTL
X
X#ifndef O_CREAT
X#define O_CREAT 01000
X#endif
X
Xextern char *__rmt_path;
X
X#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
X#include <string.h>
X#ifndef index
X#define index strchr
X#endif
X#else
Xextern char *index ();
X#endif
X
X#define _remdev(path) (!f_force_local && (__rmt_path=index(path, ':')))
X#define _isrmt(fd) ((fd) >= __REM_BIAS)
X
X#define rmtopen(path,oflag,mode) (_remdev(path) ? __rmt_open(path, oflag, mode, __REM_BIAS) : open(path, oflag, mode))
X#define rmtaccess(path, amode) (_remdev(path) ? 0 : access(path, amode))
X#define rmtstat(path, buf) (_remdev(path) ? (errno = EOPNOTSUPP), -1 : stat(path, buf))
X#define rmtcreat(path, mode) (_remdev(path) ? __rmt_open (path, 1 | O_CREAT, mode, __REM_BIAS) : creat(path, mode))
X#define rmtlstat(path,buf) (_remdev(path) ? (errno = EOPNOTSUPP), -1 : lstat(path,buf))
X
X#define rmtread(fd, buf, n) (_isrmt(fd) ? __rmt_read(fd - __REM_BIAS, buf, n) : read(fd, buf, n))
X#define rmtwrite(fd, buf, n) (_isrmt(fd) ? __rmt_write(fd - __REM_BIAS, buf, n) : write(fd, buf, n))
X#define rmtlseek(fd, off, wh) (_isrmt(fd) ? __rmt_lseek(fd - __REM_BIAS, off, wh) : lseek(fd, off, wh))
X#define rmtclose(fd) (_isrmt(fd) ? __rmt_close(fd - __REM_BIAS) : close(fd))
X#ifdef RMTIOCTL
X#define rmtioctl(fd,req,arg) (_isrmt(fd) ? __rmt_ioctl(fd - __REM_BIAS, req, arg) : ioctl(fd, req, arg))
X#else
X#define rmtioctl(fd,req,arg) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : ioctl(fd, req, arg))
X#endif
X#define rmtdup(fd) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : dup(fd))
X#define rmtfstat(fd, buf) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : fstat(fd, buf))
X#define rmtfcntl(fd,cmd,arg) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : fcntl (fd, cmd, arg))
X#define rmtisatty(fd) (_isrmt(fd) ? 0 : isatty(fd))
X
X#undef RMTIOCTL
X
Xint __rmt_open ();
Xint __rmt_close ();
Xint __rmt_read ();
Xint __rmt_write ();
Xlong __rmt_lseek ();
Xint __rmt_ioctl ();
X#endif /* !NO_REMOTE */
END_OF_FILE
if test 3321 -ne `wc -c <'rmt.h'`; then
echo shar: \"'rmt.h'\" unpacked with wrong size!
fi
# end of 'rmt.h'
fi
if test -f 'rmt.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'rmt.c'\"
else
echo shar: Extracting \"'rmt.c'\" \(5987 characters\)
sed "s/^X//" >'rmt.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1983 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley. The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1983 Regents of the University of California.\n\
X All rights reserved.\n";
X#endif /* not lint */
X
X/*
X * rmt
X */
X#include <stdio.h>
X#include <sgtty.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#ifdef HAVE_SYS_GENTAPE_H /* e.g., ISC UNIX */
X#include <sys/gentape.h>
X#else
X#include <sys/mtio.h>
X#endif
X#include <errno.h>
X
X#if defined (_I386) && defined (_AIX)
X#include <fcntl.h>
X#endif
X
X#ifdef HAVE_UNISTD_H
X#include <unistd.h>
X#else
Xlong lseek ();
X#endif
X
X#ifdef STDC_HEADERS
X#include <string.h>
X#include <stdlib.h>
X#else
Xextern char *malloc ();
X#endif
X
Xint tape = -1;
X
Xchar *record;
Xint maxrecsize = -1;
Xchar *checkbuf ();
Xvoid getstring ();
Xvoid error ();
X
X#define SSIZE 64
Xchar device[SSIZE];
Xchar count[SSIZE], mode[SSIZE], pos[SSIZE], op[SSIZE];
X
Xextern errno;
Xextern char *sys_errlist[];
Xchar resp[BUFSIZ];
X
XFILE *debug;
X#define DEBUG(f) if (debug) fprintf(debug, f)
X#define DEBUG1(f,a) if (debug) fprintf(debug, f, a)
X#define DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2)
X
Xint
Xmain (argc, argv)
X int argc;
X char **argv;
X{
X int rval;
X char c;
X int n, i, cc;
X
X argc--, argv++;
X if (argc > 0)
X {
X debug = fopen (*argv, "w");
X if (debug == 0)
X exit (1);
X (void) setbuf (debug, (char *) 0);
X }
Xtop:
X errno = 0;
X rval = 0;
X if (read (0, &c, 1) != 1)
X exit (0);
X switch (c)
X {
X
X case 'O':
X if (tape >= 0)
X (void) close (tape);
X getstring (device);
X getstring (mode);
X DEBUG2 ("rmtd: O %s %s\n", device, mode);
X#if defined (i386) && defined (AIX)
X /* This is alleged to fix a byte ordering problem. */
X /* I'm quite suspicious if it's right. -- mib */
X {
X int oflag = atoi (mode);
X int nflag = 0;
X if ((oflag & 3) == 0)
X nflag |= O_RDONLY;
X if (oflag & 1)
X nflag |= O_WRONLY;
X if (oflag & 2)
X nflag |= O_RDWR;
X if (oflag & 0x0008)
X nflag |= O_APPEND;
X if (oflag & 0x0200)
X nflag |= O_CREAT;
X if (oflag & 0x0400)
X nflag |= O_TRUNC;
X if (oflag & 0x0800)
X nflag |= O_EXCL;
X tape = open (device, nflag, 0666);
X }
X#else
X tape = open (device, atoi (mode), 0666);
X#endif
X if (tape < 0)
X goto ioerror;
X goto respond;
X
X case 'C':
X DEBUG ("rmtd: C\n");
X getstring (device); /* discard */
X if (close (tape) < 0)
X goto ioerror;
X tape = -1;
X goto respond;
X
X case 'L':
X getstring (count);
X getstring (pos);
X DEBUG2 ("rmtd: L %s %s\n", count, pos);
X rval = lseek (tape, (long) atoi (count), atoi (pos));
X if (rval < 0)
X goto ioerror;
X goto respond;
X
X case 'W':
X getstring (count);
X n = atoi (count);
X DEBUG1 ("rmtd: W %s\n", count);
X record = checkbuf (record, n);
X for (i = 0; i < n; i += cc)
X {
X cc = read (0, &record[i], n - i);
X if (cc <= 0)
X {
X DEBUG ("rmtd: premature eof\n");
X exit (2);
X }
X }
X rval = write (tape, record, n);
X if (rval < 0)
X goto ioerror;
X goto respond;
X
X case 'R':
X getstring (count);
X DEBUG1 ("rmtd: R %s\n", count);
X n = atoi (count);
X record = checkbuf (record, n);
X rval = read (tape, record, n);
X if (rval < 0)
X goto ioerror;
X (void) sprintf (resp, "A%d\n", rval);
X (void) write (1, resp, strlen (resp));
X (void) write (1, record, rval);
X goto top;
X
X case 'I':
X getstring (op);
X getstring (count);
X DEBUG2 ("rmtd: I %s %s\n", op, count);
X#ifdef MTIOCTOP
X {
X struct mtop mtop;
X mtop.mt_op = atoi (op);
X mtop.mt_count = atoi (count);
X if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
X goto ioerror;
X rval = mtop.mt_count;
X }
X#endif
X goto respond;
X
X case 'S': /* status */
X DEBUG ("rmtd: S\n");
X {
X#ifdef MTIOCGET
X struct mtget mtget;
X if (ioctl (tape, MTIOCGET, (char *) &mtget) < 0)
X goto ioerror;
X rval = sizeof (mtget);
X (void) sprintf (resp, "A%d\n", rval);
X (void) write (1, resp, strlen (resp));
X (void) write (1, (char *) &mtget, sizeof (mtget));
X#endif
X goto top;
X }
X
X default:
X DEBUG1 ("rmtd: garbage command %c\n", c);
X exit (3);
X }
Xrespond:
X DEBUG1 ("rmtd: A %d\n", rval);
X (void) sprintf (resp, "A%d\n", rval);
X (void) write (1, resp, strlen (resp));
X goto top;
Xioerror:
X error (errno);
X goto top;
X}
X
Xvoid
Xgetstring (bp)
X char *bp;
X{
X int i;
X char *cp = bp;
X
X for (i = 0; i < SSIZE; i++)
X {
X if (read (0, cp + i, 1) != 1)
X exit (0);
X if (cp[i] == '\n')
X break;
X }
X cp[i] = '\0';
X}
X
Xchar *
Xcheckbuf (record, size)
X char *record;
X int size;
X{
X if (size <= maxrecsize)
X return (record);
X if (record != 0)
X free (record);
X record = malloc (size);
X if (record == 0)
X {
X DEBUG ("rmtd: cannot allocate buffer space\n");
X exit (4);
X }
X maxrecsize = size;
X#ifdef SO_RCVBUF
X while (size > 1024 &&
X setsockopt (0, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof (size)) < 0)
X size -= 1024;
X#else
X size = 1 + ((size - 1) % 1024);
X#endif
X return (record);
X}
X
Xvoid
Xerror (num)
X int num;
X{
X
X DEBUG2 ("rmtd: E %d (%s)\n", num, sys_errlist[num]);
X (void) sprintf (resp, "E%d\n%s\n", num, sys_errlist[num]);
X (void) write (1, resp, strlen (resp));
X}
END_OF_FILE
if test 5987 -ne `wc -c <'rmt.c'`; then
echo shar: \"'rmt.c'\" unpacked with wrong size!
fi
# end of 'rmt.c'
fi
if test -f 'rtapelib.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'rtapelib.c'\"
else
echo shar: Extracting \"'rtapelib.c'\" \(14007 characters\)
sed "s/^X//" >'rtapelib.c' <<'END_OF_FILE'
X/* Functions for communicating with a remote tape drive.
X Copyright (C) 1988, 1992 Free Software Foundation, Inc.
X
X This program is free software; you can redistribute it and/or modify
X it under the terms of the GNU General Public License as published by
X the Free Software Foundation; either version 2, or (at your option)
X any later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* The man page rmt(8) for /etc/rmt documents the remote mag tape
X protocol which rdump and rrestore use. Unfortunately, the man
X page is *WRONG*. The author of the routines I'm including originally
X wrote his code just based on the man page, and it didn't work, so he
X went to the rdump source to figure out why. The only thing he had to
X change was to check for the 'F' return code in addition to the 'E',
X and to separate the various arguments with \n instead of a space. I
X personally don't think that this is much of a problem, but I wanted to
X point it out. -- Arnold Robbins
X
X Originally written by Jeff Lee, modified some by Arnold Robbins.
X Redone as a library that can replace open, read, write, etc., by
X Fred Fish, with some additional work by Arnold Robbins.
X Modified to make all rmtXXX calls into macros for speed by Jay Fenlason.
X Use -DHAVE_NETDB_H for rexec code, courtesy of Dan Kegel, srs!dan. */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <signal.h>
X
X#ifdef HAVE_SYS_MTIO_H
X#include <sys/ioctl.h>
X#include <sys/mtio.h>
X#endif
X
X#ifdef HAVE_NETDB_H
X#include <netdb.h>
X#endif
X
X#include <errno.h>
X#include <setjmp.h>
X#include <sys/stat.h>
X
X#ifndef errno
Xextern int errno;
X#endif
X
X#ifdef HAVE_UNISTD_H
X#include <unistd.h>
X#endif
X#ifdef STDC_HEADERS
X#include <string.h>
X#include <stdlib.h>
X#endif
X
X/* Maximum size of a fully qualified host name. */
X#define MAXHOSTLEN 257
X
X/* Size of buffers for reading and writing commands to rmt.
X (An arbitrary limit.) */
X#define CMDBUFSIZE 64
X
X#ifndef RETSIGTYPE
X#define RETSIGTYPE void
X#endif
X
X/* Maximum number of simultaneous remote tape connections.
X (Another arbitrary limit.) */
X#define MAXUNIT 4
X
X/* Return the parent's read side of remote tape connection FILDES. */
X#define READ(fildes) (from_rmt[fildes][0])
X
X/* Return the parent's write side of remote tape connection FILDES. */
X#define WRITE(fildes) (to_rmt[fildes][1])
X
X/* The pipes for receiving data from remote tape drives. */
Xstatic int from_rmt[MAXUNIT][2] =
X{-1, -1, -1, -1, -1, -1, -1, -1};
X
X/* The pipes for sending data to remote tape drives. */
Xstatic int to_rmt[MAXUNIT][2] =
X{-1, -1, -1, -1, -1, -1, -1, -1};
X
X/* Temporary variable used by macros in rmt.h. */
Xchar *__rmt_path;
X
X/* Close remote tape connection FILDES. */
X
Xstatic void
X_rmt_shutdown (fildes)
X int fildes;
X{
X close (READ (fildes));
X close (WRITE (fildes));
X READ (fildes) = -1;
X WRITE (fildes) = -1;
X}
X
X/* Attempt to perform the remote tape command specified in BUF
X on remote tape connection FILDES.
X Return 0 if successful, -1 on error. */
X
Xstatic int
Xcommand (fildes, buf)
X int fildes;
X char *buf;
X{
X register int buflen;
X RETSIGTYPE (*pipe_handler) ();
X
X /* Save the current pipe handler and try to make the request. */
X
X pipe_handler = signal (SIGPIPE, SIG_IGN);
X buflen = strlen (buf);
X if (write (WRITE (fildes), buf, buflen) == buflen)
X {
X signal (SIGPIPE, pipe_handler);
X return 0;
X }
X
X /* Something went wrong. Close down and go home. */
X
X signal (SIGPIPE, pipe_handler);
X _rmt_shutdown (fildes);
X errno = EIO;
X return -1;
X}
X
X/* Read and return the status from remote tape connection FILDES.
X If an error occurred, return -1 and set errno. */
X
Xstatic int
Xstatus (fildes)
X int fildes;
X{
X int i;
X char c, *cp;
X char buffer[CMDBUFSIZE];
X
X /* Read the reply command line. */
X
X for (i = 0, cp = buffer; i < CMDBUFSIZE; i++, cp++)
X {
X if (read (READ (fildes), cp, 1) != 1)
X {
X _rmt_shutdown (fildes);
X errno = EIO;
X return -1;
X }
X if (*cp == '\n')
X {
X *cp = '\0';
X break;
X }
X }
X
X if (i == CMDBUFSIZE)
X {
X _rmt_shutdown (fildes);
X errno = EIO;
X return -1;
X }
X
X /* Check the return status. */
X
X for (cp = buffer; *cp; cp++)
X if (*cp != ' ')
X break;
X
X if (*cp == 'E' || *cp == 'F')
X {
X errno = atoi (cp + 1);
X /* Skip the error message line. */
X while (read (READ (fildes), &c, 1) == 1)
X if (c == '\n')
X break;
X
X if (*cp == 'F')
X _rmt_shutdown (fildes);
X
X return -1;
X }
X
X /* Check for mis-synced pipes. */
X
X if (*cp != 'A')
X {
X _rmt_shutdown (fildes);
X errno = EIO;
X return -1;
X }
X
X /* Got an `A' (success) response. */
X return atoi (cp + 1);
X}
X
X#ifdef HAVE_NETDB_H
X/* Execute /etc/rmt as user USER on remote system HOST using rexec.
X Return a file descriptor of a bidirectional socket for stdin and stdout.
X If USER is NULL, or an empty string, use the current username.
X
X By default, this code is not used, since it requires that
X the user have a .netrc file in his/her home directory, or that the
X application designer be willing to have rexec prompt for login and
X password info. This may be unacceptable, and .rhosts files for use
X with rsh are much more common on BSD systems. */
X
Xstatic int
X_rmt_rexec (host, user)
X char *host;
X char *user;
X{
X struct servent *rexecserv;
X int save_stdin = dup (fileno (stdin));
X int save_stdout = dup (fileno (stdout));
X int tape_fd; /* Return value. */
X
X /* When using cpio -o < filename, stdin is no longer the tty.
X But the rexec subroutine reads the login and the passwd on stdin,
X to allow remote execution of the command.
X So, reopen stdin and stdout on /dev/tty before the rexec and
X give them back their original value after. */
X if (freopen ("/dev/tty", "r", stdin) == NULL)
X freopen ("/dev/null", "r", stdin);
X if (freopen ("/dev/tty", "w", stdout) == NULL)
X freopen ("/dev/null", "w", stdout);
X
X rexecserv = getservbyname ("exec", "tcp");
X if (NULL == rexecserv)
X {
X fprintf (stderr, "exec/tcp: service not available");
X exit (1);
X }
X if (user != NULL && *user == '\0')
X user = NULL;
X tape_fd = rexec (&host, rexecserv->s_port, user, NULL,
X "/etc/rmt", (int *) NULL);
X fclose (stdin);
X fdopen (save_stdin, "r");
X fclose (stdout);
X fdopen (save_stdout, "w");
X
X return tape_fd;
X}
X
X#endif /* HAVE_NETDB_H */
X
X/* Open a magtape device on the system specified in PATH, as the given user.
X PATH has the form `[user@]system:/dev/????'.
X If COMPAT is defined, it can also have the form `system[.user]:/dev/????'.
X
X OFLAG is O_RDONLY, O_WRONLY, etc.
X MODE is ignored; 0666 is always used.
X
X If successful, return the remote tape pipe number plus BIAS.
X On error, return -1. */
X
Xint
X__rmt_open (path, oflag, mode, bias)
X char *path;
X int oflag;
X int mode;
X int bias;
X{
X int i, rc;
X char buffer[CMDBUFSIZE]; /* Command buffer. */
X char system[MAXHOSTLEN]; /* The remote host name. */
X char device[CMDBUFSIZE]; /* The remote device name. */
X char login[CMDBUFSIZE]; /* The remote user name. */
X char *sys, *dev, *user; /* For copying into the above buffers. */
X
X sys = system;
X dev = device;
X user = login;
X
X /* Find an unused pair of file descriptors. */
X
X for (i = 0; i < MAXUNIT; i++)
X if (READ (i) == -1 && WRITE (i) == -1)
X break;
X
X if (i == MAXUNIT)
X {
X errno = EMFILE;
X return -1;
X }
X
X /* Pull apart the system and device, and optional user.
X Don't munge the original string. */
X
X while (*path != '@'
X#ifdef COMPAT
X && *path != '.'
X#endif
X && *path != ':')
X {
X *sys++ = *path++;
X }
X *sys = '\0';
X path++;
X
X if (*(path - 1) == '@')
X {
X /* Saw user part of user@host. Start over. */
X strcpy (user, system);
X sys = system;
X while (*path != ':')
X {
X *sys++ = *path++;
X }
X *sys = '\0';
X path++;
X }
X#ifdef COMPAT
X else if (*(path - 1) == '.')
X {
X while (*path != ':')
X {
X *user++ = *path++;
X }
X *user = '\0';
X path++;
X }
X#endif
X else
X *user = '\0';
X
X while (*path)
X {
X *dev++ = *path++;
X }
X *dev = '\0';
X
X#ifdef HAVE_NETDB_H
X /* Execute the remote command using rexec. */
X READ (i) = WRITE (i) = _rmt_rexec (system, login);
X if (READ (i) < 0)
X return -1;
X#else /* !HAVE_NETDB_H */
X /* Set up the pipes for the `rsh' command, and fork. */
X
X if (pipe (to_rmt[i]) == -1 || pipe (from_rmt[i]) == -1)
X return -1;
X
X rc = fork ();
X if (rc == -1)
X return -1;
X
X if (rc == 0)
X {
X /* Child. */
X close (0);
X dup (to_rmt[i][0]);
X close (to_rmt[i][0]);
X close (to_rmt[i][1]);
X
X close (1);
X dup (from_rmt[i][1]);
X close (from_rmt[i][0]);
X close (from_rmt[i][1]);
X
X setuid (getuid ());
X setgid (getgid ());
X
X if (*login)
X {
X execl ("/usr/ucb/rsh", "rsh", system, "-l", login,
X "/etc/rmt", (char *) 0);
X execl ("/usr/bin/remsh", "remsh", system, "-l", login,
X "/etc/rmt", (char *) 0);
X execl ("/usr/bin/rsh", "rsh", system, "-l", login,
X "/etc/rmt", (char *) 0);
X execl ("/usr/bsd/rsh", "rsh", system, "-l", login,
X "/etc/rmt", (char *) 0);
X execl ("/usr/bin/nsh", "nsh", system, "-l", login,
X "/etc/rmt", (char *) 0);
X }
X else
X {
X execl ("/usr/ucb/rsh", "rsh", system,
X "/etc/rmt", (char *) 0);
X execl ("/usr/bin/remsh", "remsh", system,
X "/etc/rmt", (char *) 0);
X execl ("/usr/bin/rsh", "rsh", system,
X "/etc/rmt", (char *) 0);
X execl ("/usr/bsd/rsh", "rsh", system,
X "/etc/rmt", (char *) 0);
X execl ("/usr/bin/nsh", "nsh", system,
X "/etc/rmt", (char *) 0);
X }
X
X /* Bad problems if we get here. */
X
X perror ("cannot execute remote shell");
X _exit (1);
X }
X
X /* Parent. */
X close (to_rmt[i][0]);
X close (from_rmt[i][1]);
X#endif /* !HAVE_NETDB_H */
X
X /* Attempt to open the tape device. */
X
X sprintf (buffer, "O%s\n%d\n", device, oflag);
X if (command (i, buffer) == -1 || status (i) == -1)
X return -1;
X
X return i + bias;
X}
X
X/* Close remote tape connection FILDES and shut down.
X Return 0 if successful, -1 on error. */
X
Xint
X__rmt_close (fildes)
X int fildes;
X{
X int rc;
X
X if (command (fildes, "C\n") == -1)
X return -1;
X
X rc = status (fildes);
X _rmt_shutdown (fildes);
X return rc;
X}
X
X/* Read up to NBYTE bytes into BUF from remote tape connection FILDES.
X Return the number of bytes read on success, -1 on error. */
X
Xint
X__rmt_read (fildes, buf, nbyte)
X int fildes;
X char *buf;
X unsigned int nbyte;
X{
X int rc, i;
X char buffer[CMDBUFSIZE];
X
X sprintf (buffer, "R%d\n", nbyte);
X if (command (fildes, buffer) == -1 || (rc = status (fildes)) == -1)
X return -1;
X
X for (i = 0; i < rc; i += nbyte, buf += nbyte)
X {
X nbyte = read (READ (fildes), buf, rc - i);
X if (nbyte <= 0)
X {
X _rmt_shutdown (fildes);
X errno = EIO;
X return -1;
X }
X }
X
X return rc;
X}
X
X/* Write NBYTE bytes from BUF to remote tape connection FILDES.
X Return the number of bytes written on success, -1 on error. */
X
Xint
X__rmt_write (fildes, buf, nbyte)
X int fildes;
X char *buf;
X unsigned int nbyte;
X{
X char buffer[CMDBUFSIZE];
X RETSIGTYPE (*pipe_handler) ();
X
X sprintf (buffer, "W%d\n", nbyte);
X if (command (fildes, buffer) == -1)
X return -1;
X
X pipe_handler = signal (SIGPIPE, SIG_IGN);
X if (write (WRITE (fildes), buf, nbyte) == nbyte)
X {
X signal (SIGPIPE, pipe_handler);
X return status (fildes);
X }
X
X /* Write error. */
X signal (SIGPIPE, pipe_handler);
X _rmt_shutdown (fildes);
X errno = EIO;
X return -1;
X}
X
X/* Perform an imitation lseek operation on remote tape connection FILDES.
X Return the new file offset if successful, -1 if on error. */
X
Xlong
X__rmt_lseek (fildes, offset, whence)
X int fildes;
X long offset;
X int whence;
X{
X char buffer[CMDBUFSIZE];
X
X sprintf (buffer, "L%ld\n%d\n", offset, whence);
X if (command (fildes, buffer) == -1)
X return -1;
X
X return status (fildes);
X}
X
X/* Perform a raw tape operation on remote tape connection FILDES.
X Return the results of the ioctl, or -1 on error. */
X
X#ifdef MTIOCTOP
Xint
X__rmt_ioctl (fildes, op, arg)
X int fildes, op;
X char *arg;
X{
X char c;
X int rc, cnt;
X char buffer[CMDBUFSIZE];
X
X switch (op)
X {
X default:
X errno = EINVAL;
X return -1;
X
X case MTIOCTOP:
X /* MTIOCTOP is the easy one. Nothing is transfered in binary. */
X sprintf (buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op,
X ((struct mtop *) arg)->mt_count);
X if (command (fildes, buffer) == -1)
X return -1;
X return status (fildes); /* Return the count. */
X
X case MTIOCGET:
X /* Grab the status and read it directly into the structure.
X This assumes that the status buffer is not padded
X and that 2 shorts fit in a long without any word
X alignment problems; i.e., the whole struct is contiguous.
X NOTE - this is probably NOT a good assumption. */
X
X if (command (fildes, "S") == -1 || (rc = status (fildes)) == -1)
X return -1;
X
X for (; rc > 0; rc -= cnt, arg += cnt)
X {
X cnt = read (READ (fildes), arg, rc);
X if (cnt <= 0)
X {
X _rmt_shutdown (fildes);
X errno = EIO;
X return -1;
X }
X }
X
X /* Check for byte position. mt_type is a small integer field
X (normally) so we will check its magnitude. If it is larger than
X 256, we will assume that the bytes are swapped and go through
X and reverse all the bytes. */
X
X if (((struct mtget *) arg)->mt_type < 256)
X return 0;
X
X for (cnt = 0; cnt < rc; cnt += 2)
X {
X c = arg[cnt];
X arg[cnt] = arg[cnt + 1];
X arg[cnt + 1] = c;
X }
X
X return 0;
X }
X}
X
X#endif
END_OF_FILE
if test 14007 -ne `wc -c <'rtapelib.c'`; then
echo shar: \"'rtapelib.c'\" unpacked with wrong size!
fi
# end of 'rtapelib.c'
fi
if test -f 'msd_dir.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'msd_dir.h'\"
else
echo shar: Extracting \"'msd_dir.h'\" \(1060 characters\)
sed "s/^X//" >'msd_dir.h' <<'END_OF_FILE'
X/*
X * @(#)msd_dir.h 1.4 87/11/06 Public Domain.
X *
X * A public domain implementation of BSD directory routines for
X * MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield),
X * August 1897
X */
X
X#define rewinddir(dirp) seekdir(dirp, 0L)
X
X#define MAXNAMLEN 12
X
X#ifdef __TURBOC__
Xtypedef int ino_t;
Xtypedef int dev_t;
X#endif
X
Xstruct dirent
X {
X ino_t d_ino; /* a bit of a farce */
X int d_reclen; /* more farce */
X int d_namlen; /* length of d_name */
X char d_name[MAXNAMLEN + 1]; /* garentee null termination */
X };
X
Xstruct _dircontents
X {
X char *_d_entry;
X struct _dircontents *_d_next;
X };
X
Xtypedef struct _dirdesc
X {
X int dd_id; /* uniquely identify each open directory */
X long dd_loc; /* where we are in directory entry is this */
X struct _dircontents *dd_contents; /* pointer to contents of dir */
X struct _dircontents *dd_cp; /* pointer to current position */
X } DIR;
X
Xextern DIR *opendir ();
Xextern struct dirent *readdir ();
Xextern void seekdir ();
Xextern long telldir ();
Xextern void closedir ();
END_OF_FILE
if test 1060 -ne `wc -c <'msd_dir.h'`; then
echo shar: \"'msd_dir.h'\" unpacked with wrong size!
fi
# end of 'msd_dir.h'
fi
if test -f 'msd_dir.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'msd_dir.c'\"
else
echo shar: Extracting \"'msd_dir.c'\" \(4500 characters\)
sed "s/^X//" >'msd_dir.c' <<'END_OF_FILE'
X/*
X * @(#)msd_dir.c 1.4 87/11/06 Public Domain.
X *
X * A public domain implementation of BSD directory routines for
X * MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield),
X * August 1897
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include "msd_dir.h"
X#ifndef __TURBOC__
X#include <malloc.h>
X#else
X#include <stdlib.h>
X#endif
X#include <string.h>
X#include <dos.h>
X
X#ifndef NULL
X# define NULL 0
X#endif /* NULL */
X
X#ifndef MAXPATHLEN
X# define MAXPATHLEN 255
X#endif /* MAXPATHLEN */
X
X/* attribute stuff */
X#define A_RONLY 0x01
X#define A_HIDDEN 0x02
X#define A_SYSTEM 0x04
X#define A_LABEL 0x08
X#define A_DIR 0x10
X#define A_ARCHIVE 0x20
X
X/* dos call values */
X#define DOSI_FINDF 0x4e
X#define DOSI_FINDN 0x4f
X#define DOSI_SDTA 0x1a
X
X#define Newisnull(a, t) ((a = (t *) malloc(sizeof(t))) == (t *) NULL)
X/* #define ATTRIBUTES (A_DIR | A_HIDDEN | A_SYSTEM) */
X#define ATTRIBUTES (A_RONLY | A_SYSTEM | A_DIR)
X
X/* what find first/next calls look use */
Xtypedef struct
X {
X char d_buf[21];
X char d_attribute;
X unsigned short d_time;
X unsigned short d_date;
X long d_size;
X char d_name[13];
X }
X
XDta_buf;
X
Xstatic char *getdirent ();
Xstatic void mysetdta ();
Xstatic void free_dircontents ();
X
Xstatic Dta_buf dtabuf;
Xstatic Dta_buf *dtapnt = &dtabuf;
Xstatic union REGS reg, nreg;
X
X#if defined(M_I86LM)
Xstatic struct SREGS sreg;
X#endif
X
XDIR *
Xopendir (name)
X char *name;
X{
X struct stat statb;
X DIR *dirp;
X char c;
X char *s;
X struct _dircontents *dp;
X char nbuf[MAXPATHLEN + 1];
X
X if (stat (name, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFDIR)
X return (DIR *) NULL;
X if (Newisnull (dirp, DIR))
X return (DIR *) NULL;
X if (*name && (c = name[strlen (name) - 1]) != '\\' && c != '/')
X (void) strcat (strcpy (nbuf, name), "\\*.*");
X else
X (void) strcat (strcpy (nbuf, name), "*.*");
X dirp->dd_loc = 0;
X mysetdta ();
X dirp->dd_contents = dirp->dd_cp = (struct _dircontents *) NULL;
X if ((s = getdirent (nbuf)) == (char *) NULL)
X return dirp;
X do
X {
X if (Newisnull (dp, struct _dircontents) || (dp->_d_entry =
X malloc ((unsigned) (strlen (s) + 1))) == (char *) NULL)
X {
X if (dp)
X free ((char *) dp);
X free_dircontents (dirp->dd_contents);
X return (DIR *) NULL;
X }
X if (dirp->dd_contents)
X dirp->dd_cp = dirp->dd_cp->_d_next = dp;
X else
X dirp->dd_contents = dirp->dd_cp = dp;
X (void) strcpy (dp->_d_entry, s);
X dp->_d_next = (struct _dircontents *) NULL;
X }
X while ((s = getdirent ((char *) NULL)) != (char *) NULL);
X dirp->dd_cp = dirp->dd_contents;
X
X return dirp;
X}
X
Xvoid
Xclosedir (dirp)
X DIR *dirp;
X{
X free_dircontents (dirp->dd_contents);
X free ((char *) dirp);
X}
X
Xstruct dirent *
Xreaddir (dirp)
X DIR *dirp;
X{
X static struct dirent dp;
X
X if (dirp->dd_cp == (struct _dircontents *) NULL)
X return (struct dirent *) NULL;
X dp.d_namlen = dp.d_reclen =
X strlen (strcpy (dp.d_name, dirp->dd_cp->_d_entry));
X strlwr (dp.d_name); /* JF */
X dp.d_ino = 0;
X dirp->dd_cp = dirp->dd_cp->_d_next;
X dirp->dd_loc++;
X
X return &dp;
X}
X
Xvoid
Xseekdir (dirp, off)
X DIR *dirp;
X long off;
X{
X long i = off;
X struct _dircontents *dp;
X
X if (off < 0)
X return;
X for (dp = dirp->dd_contents; --i >= 0 && dp; dp = dp->_d_next)
X ;
X dirp->dd_loc = off - (i + 1);
X dirp->dd_cp = dp;
X}
X
Xlong
Xtelldir (dirp)
X DIR *dirp;
X{
X return dirp->dd_loc;
X}
X
Xstatic void
Xfree_dircontents (dp)
X struct _dircontents *dp;
X{
X struct _dircontents *odp;
X
X while (dp)
X {
X if (dp->_d_entry)
X free (dp->_d_entry);
X dp = (odp = dp)->_d_next;
X free ((char *) odp);
X }
X}
X
Xstatic char *
Xgetdirent (dir)
X char *dir;
X{
X if (dir != (char *) NULL)
X { /* get first entry */
X reg.h.ah = DOSI_FINDF;
X reg.h.cl = ATTRIBUTES;
X#if defined(M_I86LM)
X reg.x.dx = FP_OFF (dir);
X sreg.ds = FP_SEG (dir);
X#else
X reg.x.dx = (unsigned) dir;
X#endif
X }
X else
X { /* get next entry */
X reg.h.ah = DOSI_FINDN;
X#if defined(M_I86LM)
X reg.x.dx = FP_OFF (dtapnt);
X sreg.ds = FP_SEG (dtapnt);
X#else
X reg.x.dx = (unsigned) dtapnt;
X#endif
X }
X#if defined(M_I86LM)
X intdosx (®, &nreg, &sreg);
X#else
X intdos (®, &nreg);
X#endif
X if (nreg.x.cflag)
X return (char *) NULL;
X
X return dtabuf.d_name;
X}
X
Xstatic void
Xmysetdta ()
X{
X reg.h.ah = DOSI_SDTA;
X#if defined(M_I86LM)
X reg.x.dx = FP_OFF (dtapnt);
X sreg.ds = FP_SEG (dtapnt);
X intdosx (®, &nreg, &sreg);
X#else
X reg.x.dx = (int) dtapnt;
X intdos (®, &nreg);
X#endif
X}
END_OF_FILE
if test 4500 -ne `wc -c <'msd_dir.c'`; then
echo shar: \"'msd_dir.c'\" unpacked with wrong size!
fi
# end of 'msd_dir.c'
fi
if test -f 'tcexparg.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'tcexparg.c'\"
else
echo shar: Extracting \"'tcexparg.c'\" \(6079 characters\)
sed "s/^X//" >'tcexparg.c' <<'END_OF_FILE'
X/* tcexparg.c - Unix-style command line wildcards for Turbo C 2.0
X
X This file is in the public domain.
X
X Compile your main program with -Dmain=_main and link with this file.
X
X After that, it is just as if the operating system had expanded the
X arguments, except that they are not sorted. The program name and all
X arguments that are expanded from wildcards are lowercased.
X
X Syntax for wildcards:
X * Matches zero or more of any character (except a '.' at
X the beginning of a name).
X ? Matches any single character.
X [r3z] Matches 'r', '3', or 'z'.
X [a-d] Matches a single character in the range 'a' through 'd'.
X [!a-d] Matches any single character except a character in the
X range 'a' through 'd'.
X
X The period between the filename root and its extension need not be
X given explicitly. Thus, the pattern `a*e' will match 'abacus.exe'
X and 'axyz.e' as well as 'apple'. Comparisons are not case sensitive.
X
X Authors:
X The expargs code is a modification of wildcard expansion code
X written for Turbo C 1.0 by
X Richard Hargrove
X Texas Instruments, Inc.
X P.O. Box 869305, m/s 8473
X Plano, Texas 75086
X 214/575-4128
X and posted to USENET in September, 1987.
X
X The wild_match code was written by Rich Salz, rsalz@bbn.com,
X posted to net.sources in November, 1986.
X
X The code connecting the two is by Mike Slomin, bellcore!lcuxa!mike2,
X posted to comp.sys.ibm.pc in November, 1988.
X
X Major performance enhancements and bug fixes, and source cleanup,
X by David MacKenzie, djm@gnu.ai.mit.edu. */
X
X#include <stdio.h>
X#include <string.h>
X#include <stdlib.h>
X#include <dos.h>
X#include <dir.h>
X
X/* Number of new arguments to allocate space for at a time. */
X#define ARGS_INCREMENT 10
X
X/* The name this program was run with, for error messages. */
Xstatic char *program_name;
X
Xstatic char **grow_argv (char **new_argv, int new_argc);
Xstatic void fatal_error (const char *message);
X
Xint wild_match (char *string, char *pattern);
Xchar *basename (char *path);
X
Xchar **expargs (int *, char **);
X
X#ifdef main
X#undef main
X#endif
X
Xint
Xmain (int argc, char **argv, char **envp)
X{
X argv = expargs (&argc, argv);
X return _main (argc, argv, envp);
X}
X
Xchar **
Xexpargs (int *pargc, char **argv)
X{
X char path[MAXPATH + 1];
X char **new_argv;
X struct ffblk block;
X char *path_base;
X char *arg_base;
X int argind;
X int new_argc;
X int path_length;
X int matched;
X
X program_name = argv[0];
X if (program_name && *program_name)
X strlwr (program_name);
X new_argv = grow_argv (NULL, 0);
X new_argv[0] = argv[0];
X new_argc = 1;
X
X for (argind = 1; argind < *pargc; ++argind)
X {
X matched = 0;
X if (strpbrk (argv[argind], "?*[") != NULL)
X {
X strncpy (path, argv[argind], MAXPATH - 3);
X path_base = basename (path);
X strcpy (path_base, "*.*");
X arg_base = argv[argind] + (path_base - path);
X
X if (!findfirst (path, &block, FA_DIREC))
X {
X strlwr (path);
X do
X {
X /* Only match "." and ".." explicitly. */
X if (*block.ff_name == '.' && *arg_base != '.')
X continue;
X path_length = stpcpy (path_base, block.ff_name) - path + 1;
X strlwr (path_base);
X if (wild_match (path, argv[argind]))
X {
X matched = 1;
X new_argv[new_argc] = (char *) malloc (path_length);
X if (new_argv[new_argc] == NULL)
X fatal_error ("memory exhausted");
X strcpy (new_argv[new_argc++], path);
X new_argv = grow_argv (new_argv, new_argc);
X }
X } while (!findnext (&block));
X }
X }
X if (matched == 0)
X new_argv[new_argc++] = argv[argind];
X new_argv = grow_argv (new_argv, new_argc);
X }
X
X *pargc = new_argc;
X new_argv[new_argc] = NULL;
X return &new_argv[0];
X}
X
X/* Return a pointer to the last element of PATH. */
X
Xchar *
Xbasename (char *path)
X{
X char *tail;
X
X for (tail = path; *path; ++path)
X if (*path == ':' || *path == '\\')
X tail = path + 1;
X return tail;
X}
X
Xstatic char **
Xgrow_argv (char **new_argv, int new_argc)
X{
X if (new_argc % ARGS_INCREMENT == 0)
X {
X new_argv = (char **) realloc
X (new_argv, sizeof (char *) * (new_argc + ARGS_INCREMENT));
X if (new_argv == NULL)
X fatal_error ("memory exhausted");
X }
X return new_argv;
X}
X
Xstatic void
Xfatal_error (const char *message)
X{
X putc ('\n', stderr);
X if (program_name && *program_name)
X {
X fputs (program_name, stderr);
X fputs (": ", stderr);
X }
X fputs (message, stderr);
X putc ('\n', stderr);
X exit (1);
X}
X
X/* Shell-style pattern matching for ?, \, [], and * characters.
X I'm putting this replacement in the public domain.
X
X Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. */
X
X/* The character that inverts a character class; '!' or '^'. */
X#define INVERT '!'
X
Xstatic int star (char *string, char *pattern);
X
X/* Return nonzero if `string' matches Unix-style wildcard pattern
X `pattern'; zero if not. */
X
Xint
Xwild_match (char *string, char *pattern)
X{
X int prev; /* Previous character in character class. */
X int matched; /* If 1, character class has been matched. */
X int reverse; /* If 1, character class is inverted. */
X
X for (; *pattern; string++, pattern++)
X switch (*pattern)
X {
X case '\\':
X /* Literal match with following character; fall through. */
X pattern++;
X default:
X if (*string != *pattern)
X return 0;
X continue;
X case '?':
X /* Match anything. */
X if (*string == '\0')
X return 0;
X continue;
X case '*':
X /* Trailing star matches everything. */
X return *++pattern ? star (string, pattern) : 1;
X case '[':
X /* Check for inverse character class. */
X reverse = pattern[1] == INVERT;
X if (reverse)
X pattern++;
X for (prev = 256, matched = 0; *++pattern && *pattern != ']';
X prev = *pattern)
X if (*pattern == '-'
X ? *string <= *++pattern && *string >= prev
X : *string == *pattern)
X matched = 1;
X if (matched == reverse)
X return 0;
X continue;
X }
X
X return *string == '\0';
X}
X
Xstatic int
Xstar (char *string, char *pattern)
X{
X while (wild_match (string, pattern) == 0)
X if (*++string == '\0')
X return 0;
X return 1;
X}
END_OF_FILE
if test 6079 -ne `wc -c <'tcexparg.c'`; then
echo shar: \"'tcexparg.c'\" unpacked with wrong size!
fi
# end of 'tcexparg.c'
fi
if test -f 'level-0' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'level-0'\"
else
echo shar: Extracting \"'level-0'\" \(6470 characters\)
sed "s/^X//" >'level-0' <<'END_OF_FILE'
X#!/bin/sh
X#
X# Run this script as root on the machine that has the tape drive, to make a
X# full (level-0) dump.
X#
X# If you give `now' as an argument, the dump is done immediately.
X# Otherwise, it waits until 1am, or until the hour given as argument.
X# Specify the hour as a number from 0 to 23.
X#
X# You must edit the file `backup-specs' to set the parameters for your site.
X
X# Useful for backup-specs, in case things have to be done slightly
X# differently for different dump levels.
XDUMP_LEVEL=0
X
X# Insure `mail' is in PATH.
XPATH="/usr/ucb:${PATH}"
Xexport PATH
X
X# This is not the most reliable test in the world. The following might be
X# more predictable:
X#
X# whoami="`whoami`"
X# euid="`sed -ne '/^'\"${whoami}\"':/{s/^[^:]*:[^:]*://;s/:.*//p;q;}' /etc/passwd`"
X# if [ "${euid}" != 0 ]; then ...
X#
Xif [ ! -w / ]; then
X echo "The backup must be run as root or else some files will fail to be dumped."
X exit 1
Xfi
X
X# Get the values of BACKUP_DIRS, BACKUP_FILES, and other variables.
X. ./backup-specs
X
X# Maybe sleep until around specified or default hour.
Xif [ "${1}" != "now" ]; then
X if [ "${1}x" != "x" ]; then
X spec="${1}"
X else
X spec="${BACKUP_HOUR}"
X fi
X
X pausetime="`date | awk '
X {
X hr = substr($4, 1, 2);
X mn = substr($4, 4, 2);
X if((hr + 0) < (spec + 0))
X print 3600 * (spec - hr) - 60 * mn;
X else
X print 3600 * (spec + (24 - hr)) - 60 * mn;
X }' spec=\"${spec}\"`"
X
X clear
X echo "${SLEEP_MESSAGE}"
X sleep "${pausetime}"
Xfi
X
X# start doing things
X
X# Put startdate in the subject line of mailed report, since if it happens
X# to run longer than 24 hours (as may be the case if someone forgets to put
X# in the next volume of the tape in adequate time), the backup date won't
X# appear too misleading.
Xstartdate="`date`"
X
Xhere="`pwd`"
X
X# Logfile name should be in the form ``log-1993-03-18-level-0''
X# i.e. year-month-date. This format is useful for sorting by name, since
X# logfiles are intentionally kept online for future reference.
XLOGFILE="log-`date | sed -ne '
X s/[^ ]* *\([^ ]*\) *\([^ ]*\).* \([^ ]*\)$/\3-\1-\2/
X /-[0-9]$/s/\([0-9]\)$/0\1/
X /Jan/{s/Jan/01/p;q;}
X /Feb/{s/Feb/02/p;q;}
X /Mar/{s/Mar/03/p;q;}
X /Apr/{s/Apr/04/p;q;}
X /May/{s/May/05/p;q;}
X /Jun/{s/Jun/06/p;q;}
X /Jul/{s/Jul/07/p;q;}
X /Aug/{s/Aug/08/p;q;}
X /Sep/{s/Sep/09/p;q;}
X /Oct/{s/Oct/10/p;q;}
X /Nov/{s/Nov/11/p;q;}
X /Dec/{s/Dec/12/p;q;}'`-level-${DUMP_LEVEL}"
X
Xlocalhost="`hostname | sed -e 's/\..*//'`"
X
XTAR_PART1="${TAR} -c --multi-volume --one-file-system --block-size=${BLOCKING} --sparse --volno-file=${VOLNO_FILE}"
X
X# Only use --info-script if DUMP_REMIND_SCRIPT was defined in backup-specs
Xif [ "x${DUMP_REMIND_SCRIPT}" != "x" ]; then
X TAR_PART1="${TAR_PART1} --info-script='${DUMP_REMIND_SCRIPT}'"
Xfi
X
X# Make sure the log file did not already exist. Create it.
X
Xif [ -f "${LOGFILE}" ] ; then
X echo "Log file ${LOGFILE} already exists." 1>&2
X exit 1
Xelse
X touch "${LOGFILE}"
Xfi
X
X# Most everything below here is run in a subshell for which all output is
X# piped through `tee' to the logfile. Doing this, instead of having
X# multiple pipelines all over the place, is cleaner and allows access to
X# the exit value from various commands more easily.
X(
X # Caveat: Some version of `mt' require `-t', not `-f'.
X mt -f "${TAPE_FILE}" rewind
X rm -f "${VOLNO_FILE}"
X
X set - ${BACKUP_DIRS}
X while [ $# -ne 0 ] ; do
X date="`date`"
X remotehost="`echo \"${1}\" | sed -e 's/:.*$//'`"
X fs="`echo \"${1}\" | sed -e 's/^.*://'`"
X fsname="`echo \"${1}\" | sed -e 's/\//:/g'`"
X
X # This filename must be absolute; it is opened on the machine that runs tar.
X TAR_PART2="--listed=/etc/tar-backup/temp.level-0"
X TAR_PART3="--label='Full backup of ${fs} on ${remotehost} at ${date}' -C ${fs} ."
X
X echo "Backing up ${1} at ${date}"
X
X # Actually back things up.
X
X if [ "z${localhost}" != "z${remotehost}" ] ; then
X rsh "${remotehost}" mkdir /etc/tar-backup > /dev/null 2>&1
X rsh "${remotehost}" rm -f /etc/tar-backup/temp.level-0
X rsh "${remotehost}" ${TAR_PART1} -f "${localhost}:${TAPE_FILE}" ${TAR_PART2} ${TAR_PART3}
X else
X mkdir /etc/tar-backup > /dev/null 2>&1
X rm -f /etc/tar-backup/temp.level-0
X # Using `sh -c exec' causes nested quoting and shell substitution
X # to be handled here in the same way rsh handles it.
X sh -c "exec ${TAR_PART1} -f \"${TAPE_FILE}\" ${TAR_PART2} ${TAR_PART3}"
X fi
X
X # `rsh' doesn't exit with the exit status of the remote command. What
X # stupid lossage. TODO: think of a reliable workaround.
X if [ $? -ne 0 ] ; then
X echo "Backup of ${1} failed." 1>&2
X # I'm assuming that the tar will have written an empty
X # file to the tape, otherwise I should do a cat here.
X else
X if [ "z${localhost}" != "z${remotehost}" ] ; then
X rsh "${remotehost}" mv -f /etc/tar-backup/temp.level-0 "/etc/tar-backup/${fsname}.level-0"
X else
X mv -f /etc/tar-backup/temp.level-0 "/etc/tar-backup/${fsname}.level-0"
X fi
X fi
X ${TAPE_STATUS}
X sleep 60
X shift
X done
X
X # Dump any individual files requested.
X
X if [ "x${BACKUP_FILES}" != "x" ] ; then
X date="`date`"
X
X TAR_PART2="--listed=/etc/tar-backup/temp.level-0"
X TAR_PART3="--label='Full backup of miscellaneous files at ${date}'"
X
X mkdir /etc/tar-backup > /dev/null 2>&1
X rm -f /etc/tar-backup/temp.level-0
X
X echo "Backing up miscellaneous files at ${date}"
X
X # Using `sh -c exec' causes nested quoting and shell substitution
X # to be handled here in the same way rsh handles it.
X sh -c "exec ${TAR_PART1} -f \"${TAPE_FILE}\" ${TAR_PART2} ${TAR_PART3} ${BACKUP_FILES}"
X
X # `rsh' doesn't exit with the exit status of the remote command. What
X # lossage. TODO: think of a reliable workaround.
X if [ $? -ne 0 ] ; then
X echo "Backup of miscellaneous files failed."
X # I'm assuming that the tar will have written an empty
X # file to the tape, otherwise I should do a cat here.
X else
X mv -f /etc/tar-backup/temp.level-0 /etc/tar-backup/misc.level-0
X fi
X ${TAPE_STATUS}
X else
X echo "No miscellaneous files specified"
X fi
X
X # Caveat: some versions of `mt' use `-t' instead of `-f'.
X mt -f "${TAPE_FILE}" rewind
X mt -f "${TAPE_FILE}" offl
X
X) 2>&1 | tee -a "${LOGFILE}"
X
Xecho "Sending the dump log to ${ADMINISTRATOR}"
Xmail -s "Results of backup started ${startdate}" ${ADMINISTRATOR} < "${LOGFILE}"
X
X# eof
END_OF_FILE
if test 6470 -ne `wc -c <'level-0'`; then
echo shar: \"'level-0'\" unpacked with wrong size!
fi
chmod +x 'level-0'
# end of 'level-0'
fi
if test -f 'level-1' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'level-1'\"
else
echo shar: Extracting \"'level-1'\" \(6590 characters\)
sed "s/^X//" >'level-1' <<'END_OF_FILE'
X#!/bin/sh
X#
X# Run this script as root on the machine that has the tape drive, to make a
X# level-1 dump containing all files changed since the last full dump.
X#
X# If you give `now' as an argument, the dump is done immediately.
X# Otherwise, it waits until 1am.
X#
X# You must edit the file `backup-specs' to set the parameters for your site.
X
X# Useful for backup-specs, in case things have to be done slightly
X# differently for different dump levels.
XDUMP_LEVEL=1
X
X# Insure `mail' is in PATH.
XPATH="/usr/ucb:${PATH}"
Xexport PATH
X
X# This is not the most reliable test in the world. The following might be
X# more predictable:
X#
X# whoami="`whoami`"
X# euid="`sed -ne '/^'\"${whoami}\"':/{s/^[^:]*:[^:]*://;s/:.*//p;q;}' /etc/passwd`"
X# if [ "${euid}" != 0 ]; then ...
X#
Xif [ ! -w / ]; then
X echo "The backup must be run as root or else some files will fail to be dumped."
X exit 1
Xfi
X
X# Get the values of BACKUP_DIRS, BACKUP_FILES, and other variables.
X. ./backup-specs
X
X# Maybe sleep until around specified or default hour.
Xif [ "z${1}" != "znow" ]; then
X if [ "${1}x" != "x" ]; then
X spec="${1}"
X else
X spec="${BACKUP_HOUR}"
X fi
X
X pausetime="`date | awk '
X {
X hr = substr($4, 1, 2);
X mn = substr($4, 4, 2);
X if((hr + 0) < (spec + 0))
X print 3600 * (spec - hr) - 60 * mn;
X else
X print 3600 * (spec + (24 - hr)) - 60 * mn;
X }' spec=\"${spec}\"`"
X
X clear
X echo "${SLEEP_MESSAGE}"
X sleep "${pausetime}"
Xfi
X
X# start doing things
X
X# Put startdate in the subject line of mailed report, since if it happens
X# to run longer than 24 hours (as may be the case if someone forgets to put
X# in the next volume of the tape in adequate time), the backup date won't
X# appear too misleading.
Xstartdate="`date`"
X
Xhere="`pwd`"
X
X# Logfile name should be in the form ``log-1993-03-18-level-1''
X# i.e. year-month-date. This format is useful for sorting by name, since
X# logfiles are intentionally kept online for future reference.
XLOGFILE="log-`date | sed -ne '
X s/[^ ]* *\([^ ]*\) *\([^ ]*\).* \([^ ]*\)$/\3-\1-\2/
X /-[0-9]$/s/\([0-9]\)$/0\1/
X /Jan/{s/Jan/01/p;q;}
X /Feb/{s/Feb/02/p;q;}
X /Mar/{s/Mar/03/p;q;}
X /Apr/{s/Apr/04/p;q;}
X /May/{s/May/05/p;q;}
X /Jun/{s/Jun/06/p;q;}
X /Jul/{s/Jul/07/p;q;}
X /Aug/{s/Aug/08/p;q;}
X /Sep/{s/Sep/09/p;q;}
X /Oct/{s/Oct/10/p;q;}
X /Nov/{s/Nov/11/p;q;}
X /Dec/{s/Dec/12/p;q;}'`-level-${DUMP_LEVEL}"
X
Xlocalhost="`hostname | sed -e 's/\..*//'`"
X
XTAR_PART1="${TAR} -c --multi-volume --one-file-system --block-size=${BLOCKING} --sparse --volno-file=${VOLNO_FILE}"
X
X# Only use --info-script if DUMP_REMIND_SCRIPT was defined in backup-specs
Xif [ "x${DUMP_REMIND_SCRIPT}" != "x" ]; then
X TAR_PART1="${TAR_PART1} --info-script='${DUMP_REMIND_SCRIPT}'"
Xfi
X
X# Make sure the log file did not already exist. Create it.
X
Xif [ -f "${LOGFILE}" ] ; then
X echo "Log file ${LOGFILE} already exists." 1>&2
X exit 1
Xelse
X touch "${LOGFILE}"
Xfi
X
X# Most everything below here is run in a subshell for which all output is
X# piped through `tee' to the logfile. Doing this, instead of having
X# multiple pipelines all over the place, is cleaner and allows access to
X# the exit value from various commands more easily.
X(
X # Caveat: Some version of `mt' require `-t', not `-f'.
X mt -f "${TAPE_FILE}" rewind
X rm -f "${VOLNO_FILE}"
X
X set - ${BACKUP_DIRS}
X while [ $# -ne 0 ] ; do
X date="`date`"
X remotehost="`echo \"${1}\" | sed -e 's/:.*$//'`"
X fs="`echo \"${1}\" | sed -e 's/^.*://'`"
X fsname="`echo \"${1}\" | sed -e 's/\//:/g'`"
X
X # This filename must be absolute; it is opened on the machine that runs tar.
X TAR_PART2="--listed=/etc/tar-backup/temp.level-1"
X TAR_PART3="--label='level 1 backup of ${fs} on ${remotehost} at ${date}' -C ${fs} ."
X
X echo "Backing up ${1} at ${date}"
X echo "Last full dump on this filesystem:"
X
X if [ "z${remotehost}" != "z${localhost}" ] ; then
X rsh "${remotehost}" "ls -l /etc/tar-backup/${fsname}.level-0; \
X cp /etc/tar-backup/${fsname}.level-0 /etc/tar-backup/temp.level-1"
X else
X ls -l "/etc/tar-backup/${fsname}.level-0"
X cp "/etc/tar-backup/${fsname}.level-0" /etc/tar-backup/temp.level-1
X fi
X
X # Actually back things up.
X
X if [ "z${remotehost}" != "z${localhost}" ] ; then
X rsh "${remotehost}" ${TAR_PART1} -f "${localhost}:${TAPE_FILE}" ${TAR_PART2} ${TAR_PART3}
X else
X # Using `sh -c exec' causes nested quoting and shell substitution
X # to be handled here in the same way rsh handles it.
X sh -c "exec ${TAR_PART1} -f \"${TAPE_FILE}\" ${TAR_PART2} ${TAR_PART3}"
X fi
X
X # `rsh' doesn't exit with the exit status of the remote command. What
X # stupid lossage. TODO: think of a reliable workaround.
X if [ $? -ne 0 ] ; then
X echo "Backup of ${1} failed."
X # I'm assuming that the tar will have written an empty
X # file to the tape, otherwise I should do a cat here.
X else
X if [ "z${localhost}" != "z${remotehost}" ] ; then
X rsh "${remotehost}" mv -f /etc/tar-backup/temp.level-1 "/etc/tar-backup/${fsname}.level-1"
X else
X mv -f /etc/tar-backup/temp.level-1 "/etc/tar-backup/${fsname}.level-1"
X fi
X fi
X ${TAPE_STATUS}
X sleep 60
X shift
X done
X
X # Dump any individual files requested.
X
X if [ "x${BACKUP_FILES}" != "x" ] ; then
X date="`date`"
X TAR_PART2="--listed=/etc/tar-backup/temp.level-1"
X TAR_PART3="--label='Incremental backup of miscellaneous files at ${date}'"
X
X echo "Backing up miscellaneous files at ${date}"
X echo "Last full dump of these files:"
X ls -l /etc/tar-backup/misc.level-0
X
X rm -f /etc/tar-backup/temp.level-1
X cp /etc/tar-backup/misc.level-0 /etc/tar-backup/temp.level-1
X
X # Using `sh -c exec' causes nested quoting and shell substitution
X # to be handled here in the same way rsh handles it.
X sh -c "exec ${TAR_PART1} -f \"${TAPE_FILE}\" ${TAR_PART2} ${TAR_PART3} ${BACKUP_FILES}"
X
X if [ $? -ne 0 ] ; then
X echo "Backup of miscellaneous files failed." 1>&2
X # I'm assuming that the tar will have written an empty
X # file to the tape, otherwise I should do a cat here.
X else
X mv -f /etc/tar-backup/temp.level-1 /etc/tar-backup/misc.level-1
X fi
X ${TAPE_STATUS}
X else
X echo "No miscellaneous files specified"
X fi
X
X # Caveat: some versions of `mt' use `-t' instead of `-f'.
X mt -f "${TAPE_FILE}" rewind
X mt -f "${TAPE_FILE}" offl
X
X) 2>&1 | tee -a "${LOGFILE}"
X
Xecho "Sending the dump log to ${ADMINISTRATOR}"
Xmail -s "Results of backup started ${startdate}" ${ADMINISTRATOR} < "${LOGFILE}"
X
X# eof
END_OF_FILE
if test 6590 -ne `wc -c <'level-1'`; then
echo shar: \"'level-1'\" unpacked with wrong size!
fi
chmod +x 'level-1'
# end of 'level-1'
fi
if test -f 'backup-specs' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'backup-specs'\"
else
echo shar: Extracting \"'backup-specs'\" \(2661 characters\)
sed "s/^X//" >'backup-specs' <<'END_OF_FILE'
X# site-specific parameters for file system backup.
X
X# User name of administrator of backups.
XADMINISTRATOR=backup-reports
X
X# Hour at which backups are normally done.
X# This should be a number from 0 to 23.
XBACKUP_HOUR=1
X
X# Location of GNU tar. This must be the same for all hosts.
XTAR=/usr/local/gnubin/tar
X
X# Device to use for dumping. It should be on the host
X# on which the dump scripts are run.
XTAPE_FILE=/dev/nrsmt0
X
X# Command to obtain status of tape drive, including error count.
X# On some tape drives there may not be such a command;
X# then simply use `TAPE_STATUS=false'.
X#
X# Might also consider
X# TAPE_STATUS="mt -f ${TAPE_FILE} status"
X# if `mts' is missing, though this alternative is rather verbose.
XTAPE_STATUS="mts -t ${TAPE_FILE}"
X
X# Blocking factor to use for writing the dump.
XBLOCKING=124
X
X# Name of temporary file to hold volume numbers. This needs to be accessible
X# by all the machines which have filesystems to be dumped.
XVOLNO_FILE=/home/gd2/dump/volnofile
X
X# Script to be run when it's time to insert a new tape in for the next
X# volume. Administrators may want to tailor this script for their site.
X# If this variable isn't set, tar will use some default behavior which is
X# probably defined in the manual.
X#DUMP_REMIND_SCRIPT='rsh apple-gunkies /home/gd2/dump/dump-remind'
X
X# List of file systems to be dumped.
X# Actually, any directory may be used, but if it has subdirectories on
X# other file systems, they are not included.
X# The host name specifies which host to run tar on.
X# It should normally be the host that actually has the file system.
X# If GNU tar is not installed on that machine, then you can specify some
X# other host which can access the file system through NFS.
X# Although these are arranged one per line, that is not mandatory.
X# It does not work to use # for comments within the string.
X
XBACKUP_DIRS='
X albert:/fs/fsf
X sugar-bombs:/fs/gd
X albert:/fs/gd2
X nutrimat:/fs/gp
X nutrimat:/fs/gp2
X albert:/fs/mailer
X placebo:/archive
X nutrimat:/fs/dist
X albert:/
X albert:/usr
X nutrimat:/
X placebo:/
X ernst:/usr1
X'
X
X# List of individual files to be dumped.
X# These should be accesible from the machine on which the dump is run.
XBACKUP_FILES=''
X
X# Message to display on the terminal while waiting for dump time. Usually
X# this will just be some literal text, preferably something more
X# entertaining than this. The awk script here saves some redundant
X# repetition, but is not really all that desirable.
XSLEEP_MESSAGE="`awk '
X BEGIN {
X for (i = 0; i < 30; i++)
X print \" \" \
X \"D O N O T T O U C H T H I S T E R M I N A L !!!!!\"
X }' /dev/null`"
X
X
X# eof
END_OF_FILE
if test 2661 -ne `wc -c <'backup-specs'`; then
echo shar: \"'backup-specs'\" unpacked with wrong size!
fi
# end of 'backup-specs'
fi
if test -f 'dump-remind' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'dump-remind'\"
else
echo shar: Extracting \"'dump-remind'\" \(2969 characters\)
sed "s/^X//" >'dump-remind' <<'END_OF_FILE'
X#!/bin/sh
X# This file is included in the GNU tar distribution as an example. It is
X# not used by default unless the proper line is uncommented in backup-specs.
X# System administrators will probably want to customize this and
X# backup-specs for their site.
X#
X# This script should be run by tar with --info-script (-F) to inform
X# interested parties that a tape for the next volume of the backup needs to
X# be put in the tape drive.
X#
X
X# Include location of `sendmail' and GNU finger.
XPATH="/usr/lib:/usr/local/gnubin:${PATH}"
Xexport PATH
X
X# Get definition of TAPE_FILE, VOLNO_FILE, and so on.
X. /home/gd2/dump/backup-specs
X
Xmt -f "${TAPE_FILE}" rewind
Xmt -f "${TAPE_FILE}" offl
X
Xvolno="`cat \"${VOLNO_FILE}\" 2> /dev/null`"
Xif [ $? -ne 0 ]; then
X volno=0
Xfi
X# Until mib fixes a bug, this needs to be incremented by one.
Xvolno=`expr ${volno} + 1`
X
X# Get a list of people to whom to mail a request for changing the tape.
X# This egregious nightmare parses the output from GNU finger which shows
X# which users are logged into consoles (and thus in the office and capable
X# of changing tapes).
X#
X# Certain users (like `root') aren't real users, and shouldn't be notified.
X# Neither should `zippy', `elvis', etc. (on the GNU machines) since they're
X# just test accounts.
Xrecipients="`
X finger .clients 2> /dev/null \
X | sed -ne '
X 1{
X /clientstatus: file has not changed in/{
X n;n;n;n;d
X }
X n;n;d
X }
X s/^..................................................//
X $!{/^$/d
X /^root?*$/d
X /^zippy$/d
X /^fnord$/d
X /^elvis$/d
X /^snurd$/d
X H
X }
X ${g
X : 1
X s/\(\n\)\([A-Za-z0-9_][A-Za-z0-9_]*\)\(\n.*\)\2\(.*\)/\1\2\3\4/g
X s/\n$//g
X t 1
X s/^\n//
X s/\n$//g
X s/\n/, /g
X : 2
X s/, ,/,/g
X t 2
X p
X }'`"
X
X# Customized behavior for FSF machines, to bring attention to the fact that
X# the tape needs to be changed (who looks at the terminal?)
Xsendmail -oi -t << __EOF__
XFrom: `basename $0` (backup tape-changing reminder)
XTo: ${recipients}
XCc: ${ADMINISTRATOR}
XSubject: Backup needs new tape for volume ${volno}
XReply-To: ${ADMINISTRATOR}
X
XThis is an automated report from the backup script running on
X`hostname`.
X
XVolume ${volno} of the backup needs to be put in the tape drive. Usually
Xwhoever prepared the backup leaves labeled tapes on top of the drive
Xitself. If there aren't any more, information about where to find tapes
Xand how to label them are posted on the wall by apple-gunkies (unhelpfully
Xobscured by a bookshelf). An online copy (which is probably more
Xup-to-date) can also be found in ~friedman/etc/fsf/backup.how.
X
X__EOF__
X
X
Xecho "Please put volume ${volno} in tape drive and press RETURN"
Xread input
Xecho "Writing volume ${volno}..."
X
Xexit 0
X
X# eof
END_OF_FILE
echo shar: 1 control character may be missing from \"'dump-remind'\"
if test 2969 -ne `wc -c <'dump-remind'`; then
echo shar: \"'dump-remind'\" unpacked with wrong size!
fi
chmod +x 'dump-remind'
# end of 'dump-remind'
fi
if test -f 'testpad.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'testpad.c'\"
else
echo shar: Extracting \"'testpad.c'\" \(1541 characters\)
sed "s/^X//" >'testpad.c' <<'END_OF_FILE'
X/* Find out if we need the pad field in the header for this machine
X Copyright (C) 1991 Free Software Foundation
X
X This program is free software; you can redistribute it and/or
X modify it under the terms of the GNU General Public License as
X published by the Free Software Foundation; either version 2, or (at
X your option) any later version.
X
X This program is distributed in the hope that it will be useful, but
X WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
X General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X*/
X
X#include <stdio.h>
X
Xstruct inc
X{
X char a[20];
X char b[20];
X};
X
Xstruct test1
X{
X char a;
X struct inc in[5];
X};
X
Xstruct test2
X{
X char a;
X char b;
X struct inc in[5];
X};
X
Xvoid
Xmain ()
X{
X struct test1 t1;
X struct test2 t2;
X int t1diff, t2diff;
X FILE *fp = fopen ("testpad.h", "w");
X
X if (fp == 0)
X {
X fprintf (stderr, "testpad: cannot open ");
X fflush (stderr);
X perror ("testpad.h");
X exit (1);
X }
X
X t1diff = (char *) &t1.in[0] - (char *) &t1;
X t2diff = (char *) &t2.in[0] - (char *) &t2;
X
X if (t2diff == t1diff + 1)
X fprintf (fp, "#define NEEDPAD\n");
X else if (t1diff != t2diff)
X fprintf (stderr, "Cannot determine padding for tar struct, \n\
Xwill try with none.\n");
X
X fclose (fp);
X exit (0);
X}
END_OF_FILE
if test 1541 -ne `wc -c <'testpad.c'`; then
echo shar: \"'testpad.c'\" unpacked with wrong size!
fi
# end of 'testpad.c'
fi
if test -f 'getpagesize.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'getpagesize.h'\"
else
echo shar: Extracting \"'getpagesize.h'\" \(610 characters\)
sed "s/^X//" >'getpagesize.h' <<'END_OF_FILE'
X#ifdef BSD
X#ifndef BSD4_1
X#define HAVE_GETPAGESIZE
X#endif
X#endif
X
X#ifndef HAVE_GETPAGESIZE
X
X#ifdef VMS
X#define getpagesize() 512
X#endif
X
X#ifdef HAVE_UNISTD_H
X#include <unistd.h>
X#endif
X
X#ifdef _SC_PAGESIZE
X#define getpagesize() sysconf(_SC_PAGESIZE)
X#else
X
X#include <sys/param.h>
X
X#ifdef EXEC_PAGESIZE
X#define getpagesize() EXEC_PAGESIZE
X#else
X#ifdef NBPG
X#define getpagesize() NBPG * CLSIZE
X#ifndef CLSIZE
X#define CLSIZE 1
X#endif /* no CLSIZE */
X#else /* no NBPG */
X#define getpagesize() NBPC
X#endif /* no NBPG */
X#endif /* no EXEC_PAGESIZE */
X#endif /* no _SC_PAGESIZE */
X
X#endif /* not HAVE_GETPAGESIZE */
X
END_OF_FILE
if test 610 -ne `wc -c <'getpagesize.h'`; then
echo shar: \"'getpagesize.h'\" unpacked with wrong size!
fi
# end of 'getpagesize.h'
fi
echo shar: End of shell archive.
exit 0